Рубрики
Без рубрики

Разделение большого класса и многократное наследование в Python

Когда я начал рефакторинг EFB Telegram Master Channel (ETM) для 2.0 обновления, я был исследован … Помечено с Python, DesignPattern, Ehforarderbot, Itchat.

Когда я начал рефакторинг EFB Telegram Master Channel (ETM) для обновлений 2.0, я исследую способы организовать код в разные файлы достойным образом. В этой статье я хотел бы поговорить о стратегии, которую я использовал, по сравнению с другой кодовой базой, я читал тогда, ИТЧАТ Отказ

В EUTM версии 1 большая часть кода написана тяжелая и уродливая 1675-линейная __init__.py Отказ Поскольку в EUTM планируется добавить больше объектов, для меня было очень сложно ориентироваться по коду, что подняло мою потребность в рефакторинге этой огромной вещи.

Тогда (который, удивительно, был более 2 лет назад ) Основная ссылка у меня была достаточно большого проекта, было ИТЧАТ . С тех пор их кодовая структура не меняется с тех пор. ИТЧАТ У них было достаточно большой репозиторий кода, но так, как он разбивает его функции, довольно Unideal.

Путь ITCHAT сделал все функционировать определенным на корневом уровне каждого файла и иметь функцию погрузчика, которая «загружает» эти методы для объекта, называемого Core который содержит некоторые данные конфигурации. В переводчик Python этот метод действительно работает благодаря своей динамической печати. Но это выглядит очень плохо, когда вы пытались работать с кодом, поскольку IDE обычно не может дать никакого намека с объектами, определенными таким образом. Это также происходит, когда вы пытаетесь работать над самой библиотекой, несмотря на все функцию начинается с Я в их аргументах.

Затем я продолжал искать другие распространенные практики на разрушении большого класса, некоторые предложили импортирующие функции внутри функции, другой используют несколько наследований. [ Реф. ] Первый не сильно отличается от того, что ИТЧАТ Делал, а последний выглядел многообещающим в начале. Я продолжал сделать некоторый эксперимент с несколькими наследованием, и обнаружил, что он обеспечивает лучшую автозаполнение с IDE, но только в основном классе. Я не вижу один подкласс от другого в IDE. Это все еще разумно, так как все эти подклассы объединяются только в основном классе, они не знают друг о друге.

# core.py
from .components import load_components


class Core:
    def method_1(self, param_1, param_2, param_3):
        """Doc string goes here."""
        raise NotImplementedError()

    def method_2(self):
        """Doc string goes here."""
        raise NotImplementedError()

    def method_3(self, param_1):
        """Doc string goes here."""
        raise NotImplementedError()


load_components(Core)
# components/__init__.py
from .component_1 import load_component_1
from .component_2 import load_component_2


def load_components(core):
    load_component_1(core)
    load_component_2(core)
# components/component_1.py
def load_contact(core):
    core.method_1 = method_1
    core.method_2 = method_2


def method_1(self, param_1, param_2, param_3):
    # Actual implementation
    ...


def method_2(self):
    # Actual implementation
    ...
# components/component_2.py
def load_contact(core):
    core.method_3 = method_3


def method_3(self, param_1):
    # Actual implementation
    ...

Я думал себе, почему я не могу просто сделать еще несколько классов и позволить им ссылаться друг с другом? Оказывается, что работал довольно хорошо для меня. Я разделил свои функции на несколько различных классов «менеджера», каждый из которых инициализируется со ссылкой на основной класс. Эти классы создаются в топологическом порядке, так что классы, упомянутые другими, созданы ранее. В EUTM классы, которые упоминаются, обычно относятся к этим утилитам поставщиков данных, а именно ЭКСПЕРИМЕНТАЛЬНЫЙФОРМАГСМАНАГЕР С DatabaseMaserager и TelegrambotManager .

# __init__.py
from .flags import ExperimentalFlagsManager
from .db import DatabaseManager
from .chat_binding import ChatBindingManager

class TelegramChannel():
    def __init__(self):
        self.flags: ExperimentalFlagsManager = ExperimentalFlagsManager(self)
        self.db: DatabaseManager = DatabaseManager(self)
        self.chat_binding: ChatBindingManager = ChatBindingManager(self)
# flags.py
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    # Avoid cycle import for type checking
    from . import TelegramChannel


class ExperimentalFlagsManager:
    def __init__(channel: 'TelegramChannel'):
        self.channel = channel
        ...
# db.py
from typing import TYPE_CHECKING
from .flags import ExperimentalFlagsManager

if TYPE_CHECKING:
    # Avoid cycle import for type checking
    from . import TelegramChannel


class DatabaseManager:
    def __init__(channel: 'TelegramChannel'):
        self.channel: 'TelegramChannel' = channel
        self.flags: ExperimentalFlagsManager = channel.flags
        ...
# chat_binding.py
from typing import TYPE_CHECKING
from .chat_binding import ChatBindingManager
from .db import DatabaseManager

if TYPE_CHECKING:
    # Avoid cycle import for type checking
    from . import TelegramChannel


class ChatBindingManager:
    def __init__(channel: 'TelegramChannel'):
        self.channel: 'TelegramChannel' = channel
        self.flags: ExperimentalFlagsManager = channel.flags
        self.db: DatabaseManager = channel.db
        ...

Во время рефакторинга ETM я узнал, что множественное наследование в Python также используется другим способом – смешины. Микс – это классы, которые полезны, когда вы хотите добавить набор функций во многих других классах. Это просветил меня, когда я пытался справиться с постоянно добавлять ссылки на GetText Переводчик во всех классах менеджера.

Я добавил смешин под названием Localemixin что извлекает функции переводчика ( getText и netthtext ) Из основного класса ссылки (при условии, что они гарантированно будут там) и назначают локальное свойство, которое отражает эти методы.

class LocaleMixin:
    channel: 'TelegramChannel'

    @property
    def _(self):
        return self.channel.gettext

    @property
    def ngettext(self):
        return self.channel.ngettext

Когда классы Mixin добавляются в список унаследованных классов, IDE может правильно распознавать эти свойства вспомогательных свойств, а их определения консолидированы в одном месте. Я считаю, что это более организовано, что предыдущий стиль.

В конце концов, я обнаруживаю, что просто создание классов для каждого компонента моего кода оказывается наиболее организованным, и IDe-Fire способ для разрушения большого класса, а смешины полезны для создания ссылок или функции помощника для нескольких классов.

Пост Разделение большого класса и многократное наследование в Python появился первым на 1А23. Блог Отказ

Оригинал: “https://dev.to/blueset/splitting-a-large-class-and-multiple-inheritance-in-python-3n36”