Share This
Связаться со мной
Крути в низ
Categories
//Метаклассы в Python

Метаклассы в Python

29.03.2022Category : Python

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

Метакласс в Python — это класс классов, определяющий поведение класса. То есть класс сам по себе является экземпляром метакласса. Класс определяет поведение экземпляров этого класса. Чтобы хорошо понимать метаклассы, необходимо иметь предыдущий опыт работы с классами в Python. Поэтому, прежде чем углубиться в метаклассы, давайте рассмотрим несколько основных концепций.

От редакции Pythonist. О классах можно почитать в статье «Классы в Python».

В Python все является объектом

class TestClass():     pass  my_test_class = TestClass() print(my_test_class)  # Oytput: # <__main__.TestClass object at 0x7f6fcc6bf908>

Классы в Python можно создавать динамически

Функция type() в Python позволяет нам определить тип объекта. Давайте проверим тип объекта, который мы только что создали:

type(TestClass) # type type(type) # type

Подождите, но что это было? Мы ожидали, что тип объекта, который мы создали выше, будет классом, однако это не так. Зафиксируйте, пожалуйста, эту мысль. Мы вернемся к ней чуть позже.

Кроме того, можно заметить, что type имеет тип type. Всё потому, что это экземпляр type.

Функция type, помимо определения типа объектов, имеет еще один волшебный вариант применения. С ее помощью можно динамически создавать классы. Давайте рассмотрим, как это делается.

Показанный ниже класс Pythonist будет создан с использованием type:

class Pythonist():     pass PythonistClass = type('Pythonist', (), {}) print(PythonistClass) print(Pythonist())  # Output: # <class '__main__.DataCamp'> # <__main__.DataCamp object at 0x7f6fcc66e358>

Здесь Pythonist — это имя класса, а Pythonist Class — это переменная, содержащая ссылку на класс.

При использовании type мы можем передавать атрибуты класса с помощью словаря:

PythonClass = type('PythonClass', (), {'start_date': 'August 2018', 'instructor': 'John Doe'} ) print(PythonClass.start_date, PythonClass.instructor) print(PythonClass)  # Output: # August 2018 John Doe # <class '__main__.PythonClass'>

metaklassy v python 8299dc0 - Метаклассы в Python

Английский для программистов

Наш телеграм канал с тестами по английскому языку для программистов. Английский это часть карьеры программиста. Поэтому полезно заняться им уже сейчас

Подробнее ×

Если мы хотим, чтобы наш PythonClass наследовался от класса Pythonist, мы передаем его нашему второму аргументу при определении класса с использованием type:

PythonClass = type('PythonClass', (Pythonist,), {'start_date': 'August 2018', 'instructor': 'John Doe'} ) print(PythonClass)  # Output: # <class '__main__.PythonClass'>

Теперь, когда эти две концепции ясны, мы понимаем, что Python создает классы, используя метакласс. Мы видели, что все в Python является объектом и эти объекты создаются метаклассами.

Всякий раз, когда мы вызываем class для создания класса, есть метакласс, который колдует над созданием класса за кулисами. Мы уже видели, как это делает type. Это похоже на str, который создает строки, и int, который создает целые числа. В Python атрибут __class__ позволяет нам проверить тип текущего экземпляра. Давайте создадим строку и проверим ее тип.

article = 'metaclasses' article.__class__ # str

Мы также можем проверить тип, используя type(article) следующим образом:

type(article) # str

Проверив тип самого str, мы узнаем, что это тоже type:

type(str) # type

Если мы проверим тип float, int, list, tuple и dict с помощью type(), мы получим аналогичный результат. Это потому, что все эти объекты имеют тип type:

print(type(list),type(float), type(dict), type(tuple)) # <class 'type'> <class 'type'> <class 'type'> <class 'type'>

Итак, мы уже видели, как type создает классы. Следовательно, когда мы проверяем __class__ из __class__, он должен возвращать type:

article.__class__.__class__ # type

Создание пользовательских метаклассов

В Python мы можем настроить процесс создания класса, передав ключевое слово metaclass в определение класса. Это также можно сделать, унаследовав класс, в который уже передали это ключевое слово. К примеру, это может выглядеть следующим образом:

class MyMeta(type):     pass  class MyClass(metaclass=MyMeta):     pass  class MySubclass(MyClass):     pass

Ниже мы видим, что тип класса MyMeta — type, а тип MyClass и MySubClass — MyMeta.

print(type(MyMeta)) print(type(MyClass)) print(type(MySubclass))  # Output: # <class 'type'> # <class '__main__.MyMeta'> # <class '__main__.MyMeta'>

При определении класса и отсутствии метакласса по умолчанию будет использоваться метакласс type. Если задан метакласс, не являющийся экземпляром type(), то он используется непосредственно как метакласс.

__new__ и __init__

Метаклассы также могут быть определены одним из двух способов, показанных ниже. Давайте рассмотрим разницу между ними:

class MetaOne(type):     def __new__(cls, name, bases, dict):         pass  class MetaTwo(type):     def __init__(self, name, bases, dict):         pass

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

__init__ обычно вызывается после создания объекта для его инициализации.

Метод метакласса __call__

Согласно официальной документации, мы также можем переопределить другие методы класса. К примеру, определив собственный метод __call__() в метаклассе, что позволит настроить поведение при вызове класса.

Метод метакласса __prepare__

Согласно документам модели данных Python:

«После определения соответствующего метакласса подготавливается пространство имен класса.

Если у метакласса есть атрибут __prepare__, он вызывается как namespace = metaclass.__prepare__(name, bases, **kwds) (где дополнительные аргументы ключевого слова, если они есть, берутся из определения класса).

Если метакласс не имеет __prepare__attribute, то пространство имен класса инициализируется как пустое упорядоченное отображение».

Проектирование Singleton с использованием метакласса

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

class SingletonMeta(type):     _instances = {}     def __call__(cls, *args, **kwargs):         if cls not in cls._instances:             cls._instances[cls] = super(SingletonMeta,cls).__call__(*args, **kwargs)         return cls._instances[cls]  class SingletonClass(metaclass=SingletonMeta):     pass

Заключение

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

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

Надеемся, данная статья была вам полезна! Успехов в написании кода!

Перевод статьи «Introduction to Python Metaclasses».

metaklassy v python 9e7dd67 - Метаклассы в Python

Английский для программистов

Наш телеграм канал с тестами по английскому языку для программистов. Английский это часть карьеры программиста. Поэтому полезно заняться им уже сейчас

Подробнее ×

  • 0 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Свежие комментарии

    Рубрики

    About Author 01.

    blank
    Roman Spiridonov

    Моя специальность - Back-end Developer, Software Engineer Python. Мне 39 лет, я работаю в области информационных технологий более 5 лет. Опыт программирования на Python более 3 лет. На Django более 2 лет.

    Categories 05.

    © Speccy 2022 / All rights reserved

    Связаться со мной
    Close