Metaclass в Python: что такое метаклассы?

В этом уроке вы узнаете, что такое метаклассы в Python и поймете, как использовать metaclass для создания других классов.

Содержание

Что такое метакласс в Python?

Метакласс — это класс, который создает другие классы. По умолчанию Python использует метакласс type для создания других классов.

Например, следующий код определяет класс Person:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

Когда Python выполняет код, он использует метакласс type для создания класса Person. Причина в том, что класс Person по умолчанию использует метакласс type.

Явное определение класса Person выглядит следующим образом:

class Person(object, metaclass=type):
    def __init__(self, name, age):
        self.name = name
        self.age = age

Аргумент метакласса позволяет указать, какой класс метакласса использовать для определения класса. Таким образом, вы можете создать собственный метакласс со своей собственной логикой для создания других классов. Используя собственный метакласс, вы можете добавить функциональность в процесс создания класса.

Пример метакласса Python

  • Сначала определите собственный метакласс под названием Human, у которого атрибут freedom по умолчанию установлен в значение True:
class Human(type):
    def __new__(mcs, name, bases, class_dict):
        class_ = super().__new__(mcs, name, bases, class_dict)
        class_.freedom = True
        return class_

Обратите внимание, что метод __new__ возвращает новый класс или объект класса.

  • Во-вторых, определите класс Person, который использует метакласс Human:
class Person(object, metaclass=Human):
    def __init__(self, name, age):
        self.name = name
        self.age = age

Класс Person будет иметь атрибут freedom, как показано в переменных класса:

pprint(Person.__dict__)

Выход:

mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>,
              '__doc__': None,
              '__init__': <function Person.__init__ at 0x000001E716C71670>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              'freedom': True})

Полный код:

from pprint import pprint


class Human(type):
    def __new__(mcs, name, bases, class_dict):
        class_ = super().__new__(mcs, name, bases, class_dict)
        class_.freedom = True
        return class_


class Person(object, metaclass=Human):
    def __init__(self, name, age):
        self.name = name
        self.age = age


pprint(Person.__dict__)

Параметры метакласса

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

class Human(type):
    def __new__(mcs, name, bases, class_dict, **kwargs):
        class_ = super().__new__(mcs, name, bases, class_dict)
        if kwargs:
            for name, value in kwargs.items():
                setattr(class_, name, value)
        return class_

В следующем примере метакласс Human используется для создания класса Person с переменными класса country и freedom, со значениями USA и True соответственно:

class Person(object, metaclass=Human, country='USA', freedom=True):
    def __init__(self, name, age):
        self.name = name
        self.age = age

Вот переменные класса Person:

pprint(Person.__dict__)

Выход:

mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>,
              '__doc__': None,
              '__init__': <function Person.__init__ at 0x0000018A334235E0>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Person' objects>,
              'country': 'USA',
              'freedom': True})

Полный код:

from pprint import pprint


class Human(type):
    def __new__(mcs, name, bases, class_dict, **kwargs):
        class_ = super().__new__(mcs, name, bases, class_dict)
        if kwargs:
            for name, value in kwargs.items():
                setattr(class_, name, value)
        return class_


class Person(object, metaclass=Human, freedom=True, country='USA'):
    def __init__(self, name, age):
        self.name = name
        self.age = age


pprint(Person.__dict__)

Когда используются метаклассы в Python?

Вот цитата Тима Питерса, написавшего «Дзен Python»:

Метаклассы — глубже, чем магия, о которой 99% пользователей никогда не стоит задумываться. Если вы задаетесь вопросом, нужны ли они вам, то нет (люди, которым они действительно нужны, точно это знают, и им не нужны объяснения, почему).

Тим Питерс

На практике обычно вам не нужно использовать метаклассы, если вы не поддерживаете или не разрабатываете ядро больших фреймворков, таких как Django.

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *