Понимание подкласса init

Я, наконец, обновил свою версию python, и я обнаружил новые функции, добавленные. Помимо всего прочего, я чесал голову вокруг нового __init_subclass__ метод. Из документов:

этот метод вызывается всякий раз, когда содержащий класс подклассов. cls затем новый подкласс. Если определено как обычный метод экземпляра, это метод неявно преобразуется в метод класса.

поэтому я начал играть с ним немного, следуя примеру в документах:

class Philosopher:
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        print(f"Called __init_subclass({cls}, {default_name})")
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass

class GermanPhilosopher(Philosopher, default_name="Nietzsche"):
    default_name = "Hegel"
    print("Set name to Hegel")

Bruce = AustralianPhilosopher()
Mistery = GermanPhilosopher()
print(Bruce.default_name)
print(Mistery.default_name)

производит этот выход:

Called __init_subclass(<class '__main__.AustralianPhilosopher'>, 'Bruce')
'Set name to Hegel'
Called __init_subclass(<class '__main__.GermanPhilosopher'>, 'Nietzsche')
'Bruce'
'Nietzsche'

Я понимаю, что этот метод называется после определение подкласса, но мои вопросы, в частности об использовании этой функции. Я прочитал PEP 487 статья также, но не очень мне помогла. Где этот метод будет полезен? Это:

  • суперкласс для регистрации подклассов при творение?
  • принуждение подкласса устанавливать поле во время определения?

кроме того, мне нужно понять __set_name__ чтобы полностью понять его использование?

4 ответов


__init_subclass__ и __set_name__ являются ортогональными механизмами - они не привязаны друг к другу, просто описаны в том же PEP. Оба являются функциями, которые ранее нуждались в полнофункциональном метаклассе. РЕР 487 адреса 2 из наиболее распространенных видов использования метаклассов:

  • как сообщить родителю, когда он подкласс (__init_subclass__)
  • как сообщить классу дескрипторов имя свойства, для которого он используется (__set_name__)

As бодрость духа говорит:

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

первые две категории могут быть легко достигнуты с помощью простых крючков в создании класса:

  • An __init_subclass__ крюк, который инициализирует все подклассы данного класса.
  • при создании класса a __set_name__ hook вызывается для всех атрибутов (дескрипторов), определенных в классе, и

третья категория-это тема другого PEP,PEP 520.

обратите внимание также, что в то время как __init_subclass__ является заменой для использования метакласса в этой дерево наследования класса,__set_name__ на дескриптор класс - это замена для использования метакласса для класса, который имеет экземпляр дескриптор как атрибут.


PEP 487 устанавливает, чтобы взять два общих metaclass usecases и сделать их более доступными без необходимости понимать все входы и выходы метаклассов. Две новые функции,__init_subclass__ и __set_name__ иначе независимая, они не полагаются друг на друга.

__init_subclass__ - это просто метод крючка. Вы можете использовать его для чего угодно. Это полезно для обеих регистрации подклассов в некотором роде,и для установки значений атрибутов по умолчанию на тех подклассы.

недавно мы использовали это для предоставления "адаптеров" для различных систем управления версиями, например:

class RepositoryType(Enum):
    HG = auto()
    GIT = auto()
    SVN = auto()
    PERFORCE = auto()

class Repository():
    _registry = {t: {} for t in RepositoryType}

    def __init_subclass__(cls, scm_type=None, name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if scm_type is not None:
            cls._registry[scm_type][name] = cls

class MainHgRepository(Repository, scm_type=RepositoryType.HG, name='main'):
    pass

class GenericGitRepository(Repository, scm_type=RepositoryType.GIT):
    pass

это тривиально позволит нам определить классы обработчиков для определенных репозиториев без необходимости прибегать к использованию метакласса или декораторов.


главное __init_subclass__ было, как следует из названия ОПТОСОЗ, предложить более простую форму настройки для классов.

это крюк, который позволяет вам возиться с классами без необходимости знать о метаклассах, отслеживать все аспекты построения классов или беспокоиться о конфликтах метаклассов по линии. As ответ Ник Коглен на ранней стадии этого PEP заявляет:

основная предполагаемая читаемость/ремонтопригодность выгода от перспектива более четкого различения подкласса " customises инициализация "случай из" настраивает поведение среды выполнения дело подклассов.

полный метакласс не указаны рамки удар, пока __init_subclass__ более четко указывает, что нет постоянное влияние на создание пост-подкласса поведения.

метаклассы считаются волшебными по какой-то причине, вы не знаете, каковы их эффекты будет после того, как класс будет создан. __init_subclass__, с другой стороны, это просто еще один метод класса, он работает один раз, а затем это делается. (см. документацию для точной функциональности.)


весь смысл PEP 487 заключается в упрощении (i.e удаление необходимости использования) метаклассов для некоторых общих целей.

__init_subclass__ заботится об инициализации пост-класса в то время как __set_name__ (что имеет смысл только для классов дескрипторов) был добавлен для упрощения инициализации дескрипторов. Кроме того, они не связаны.

третий общий случай для метаклассов (сохранение порядка определения), который упоминается,также была упрощена. Это было адресовано без крючка, используя упорядоченное отображение для пространства имен (которое в Python 3.6 является dict, но это деталь реализации :-)


Я хотел бы добавить некоторые ссылки, связанные с метаклассы и __init_subclass__ Это может быть полезно.

фон

__init_subclass__ была введена в качестве альтернативы созданию метаклассы. Вот 2-минутное резюме PEP 487 на говорить одним из основных разработчиков, Бретт Кэннон.

Рекомендуемая Литература

    Гвидо ван Россума!--20-->блоге на ранняя история метаклассов в Python Джейк Vanderplas это!--24-->блоге более глубокий взгляд на реализацию метаклассов