Что такое метаклассы в Python? - PullRequest
       152

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

5145 голосов
/ 19 сентября 2008

Что такое метаклассы и для чего мы их используем?

Ответы [ 15 ]

36 голосов
/ 09 августа 2016

type на самом деле metaclass - класс, который создает другие классы. Большинство metaclass являются подклассами type. metaclass получает класс new в качестве первого аргумента и предоставляет доступ к объекту класса с подробной информацией, как указано ниже:

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
... 

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
... 
class name: NewClass
Bases <class 'object'>: 
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

Обратите внимание, что класс не был создан в любое время; простой акт создания класса запускает выполнение metaclass.

32 голосов
/ 27 декабря 2016

TL; DR версия

Функция type(obj) возвращает тип объекта.

type() класса является его метаклассом .

Чтобы использовать метакласс:

class Foo(object):
    __metaclass__ = MyMetaClass
22 голосов
/ 13 июля 2017

Классы Python сами по себе являются объектами - как, например, - их метакласса.

Метакласс по умолчанию, который применяется, когда вы определяете классы как:

class foo:
    ...

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

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

class somemeta(type):
    __new__(mcs, name, bases, clsdict):
      """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes 
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass. 
 ie: 
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}

      you can modify any of these values before passing on to type
      """
      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
      """ 
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
      """
      pass


    def __prepare__():
        """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
        """
        return dict()

    def mymethod(cls):
        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
        """
        pass

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

17 голосов
/ 12 января 2018

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

например, мы можем создать класс Hi с функцией type (), и нам не нужно использовать этот способ с классом Hi (object):

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

Помимо использования type () для динамического создания классов, вы можете управлять поведением создания класса и использовать метакласс.

Согласно объектной модели Python, класс является объектом, поэтому класс должен быть экземпляром другого определенного класса. По умолчанию класс Python является экземпляром класса type. То есть тип является метаклассом большинства встроенных классов и метаклассом пользовательских классов.

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

Магия вступит в силу, когда мы передадим аргументы ключевых слов в метаклассе, это указывает интерпретатору Python для создания CustomList через ListMetaclass. new (), на данный момент мы можем, например, изменить определение класса, добавить новый метод и затем вернуть исправленное определение.

7 голосов
/ 15 сентября 2018

В дополнение к опубликованным ответам я могу сказать, что metaclass определяет поведение класса. Таким образом, вы можете явно установить свой метакласс. Всякий раз, когда Python получает ключевое слово class, он начинает поиск metaclass. Если он не найден - тип метакласса по умолчанию используется для создания объекта класса. Используя атрибут __metaclass__, вы можете установить metaclass вашего класса:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

Будет выводиться так:

class 'type'

И, конечно, вы можете создать свой собственный metaclass, чтобы определить поведение любого класса, созданного с использованием вашего класса.

Для этого класс по умолчанию metaclass должен быть унаследован, так как это основной metaclass:

class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

Вывод будет:

class '__main__.MyMetaClass'
class 'type'
...