Питання Що таке метакласи в Python?


Які метакласи і що ми використовуємо для них?


4584
2017-09-19 06:10


походження




Відповіді:


Метаклас є класом класу. Як клас визначає, як веде себе екземпляр класу, метаклас визначає, як клас поводиться. Клас - це екземпляр метакласу.

metaclass diagram

Поки в Python ви можете використовувати довільні дані для метакласів (наприклад Єруб шоу), то більш корисним підходом є фактично зробити його дійсним класом. type це звичайний метаклас у Python. У випадку, якщо вам цікаво, так type це сам клас, і це його власний тип. Ви не зможете відтворити щось на зразок type чисто в Python, але Python чіти трохи. Щоб створити свій власний метаклас у Python, ви просто хочете до підкласу type.

Метаклас найчастіше використовується як фабрика класів. Як ви створюєте екземпляр класу, викликаючи клас, Python створює новий клас (коли він виконує оператор 'class'), викликаючи метаклас. У поєднанні з нормальним __init__ і __new__ Методи, метакласи, таким чином, дозволяють робити "додаткові речі" під час створення класу, наприклад, реєструвати новий клас у деякому реєстрі або навіть повністю замінити клас на щось інше.

Коли class Заявка виконана, Python вперше виконує тіло class заява як звичайний блок коду. Результуюче простір імен (dict) містить атрибути класу-бути. Метаклас визначається шляхом перегляду базових класів класу (метакасла успадковуються), на __metaclass__ атрибут class-to-be (якщо такий є) або __metaclass__ глобальна змінна Метаклассу потім викликають ім'я, бази та атрибути класу, щоб продемонструвати його.

Проте метакласи фактично визначають тип класу, а не просто фабрику, щоб ви могли зробити з ними багато більше. Ви можете, наприклад, визначити звичайні методи в метакласі. Ці metaclass-методи подібні до методів класу, оскільки їх можна викликати в класі без екземпляра, але вони також не схожі на кластові методи, оскільки їх не можна викликати в екземпляр класу. type.__subclasses__() є прикладом методу на type метаклас Ви також можете визначити звичайні "магічні" методи, такі як __add__, __iter__ і __getattr__, щоб впровадити або змінити поведінку класу.

Нижче наведено агрегований приклад бітів і частин:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__

2087
2017-09-19 07:01



"Ви не зможете відтворити щось подібне типу чисто в Python, але Python трохи чіти", це не так. - ppperry
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b - ppperry


Класи як об'єкти

Перш ніж розуміти метакласи, вам потрібно майстер-класи в Python. І Python має дуже своєрідне уявлення про те, які класи, запозичені з мови Smalltalk.

У більшості мов класи є лише фрагментами коду, які описують, як створювати об'єкт. Це також є істинним в Python теж:

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Але класи більше, ніж у Python. Класи є об'єктами теж.

Так, об'єкти.

Як тільки ви використовуєте ключове слово class, Python виконує його і створює ОБ'ЄКТ. Інструкція

>>> class ObjectCreator(object):
...       pass
...

створює в пам'яті об'єкт з назвою "ObjectCreator".

Цей об'єкт (клас) сам по собі здатний створювати об'єкти (екземпляри), і саме тому це клас.

Але все-таки це об'єкт, і тому:

  • ви можете призначити його для змінної
  • ви можете скопіювати його
  • ви можете додати йому атрибути
  • ви можете передавати його як параметр функції

напр .:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

Створення класів динамічно

Оскільки класи є об'єктами, ви можете створювати їх на льоту, як і будь-який об'єкт.

По-перше, ви можете створити клас у функції, використовуючи class:

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

Але це не настільки динамічно, тому що вам все одно доводиться писати весь клас сам.

Оскільки класи є об'єктами, вони повинні бути згенеровані чимось.

Коли ви використовуєте class ключове слово, Python створює цей об'єкт автоматично. Але як з більшістю речей у Python це дає вам можливість зробити це вручну.

Запам'ятайте функцію type? Стара добра функція, яка дозволяє вам знати що введіть об'єкт:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

Добре, type має зовсім іншу здатність, він також може створювати класи на льоту. type може взяти опис класу як параметри, і повернемо клас.

(Я знаю, це нерозумно, що одна і та ж функція може мати два зовсім різні способи використання відповідно до параметрів, які ви передаєте йому. Це проблема через зворотний зв'язок сумісність у Python)

type працює таким чином:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

напр .:

>>> class MyShinyClass(object):
...       pass

може бути створений вручну таким чином:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

Ви помітите, що ми використовуємо "MyShinyClass" як назву класу і як змінна для тримання посилання на клас. Вони можуть бути різними, але немає підстав ускладнювати речі.

type приймає словник для визначення атрибутів класу. Так:

>>> class Foo(object):
...       bar = True

Можна перекласти на:

>>> Foo = type('Foo', (), {'bar':True})

І використовується як звичайний клас:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

І, звичайно, ви можете успадкувати від нього, так що:

>>>   class FooChild(Foo):
...         pass

був би:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

Врешті-решт, ви захочете додати методи до свого класу. Просто визначте функцію з належним підписом і призначити його як атрибут.

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

І після того, як ви динамічно створите клас, ви зможете додати ще більше методів, як додавання методів до звичайно створеного об'єкта класу.

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

Ви бачите, куди ми йдемо: у Python класи є об'єктами, і ви можете динамічно створювати клас на льоту.

Це те, що робить Python, коли ви використовуєте ключове слово class, і це робиться за допомогою метакласу.

Що таке метакласи (нарешті)

Метакласи - "матеріал", який створює класи.

Ви визначаєте класи, щоб створити об'єкти, чи не так?

Але ми дізналися, що класи Python є об'єктами.

Ну, метакласи - це те, що створює ці об'єкти. Це класи класів, ви можете зобразити їх таким чином:

MyClass = MetaClass()
my_object = MyClass()

Ви це бачили type дозволяє вам зробити щось на зразок цього:

MyClass = type('MyClass', (), {})

Це тому, що функція type насправді є метаклассом. type є метаклас Python використовує для створення всіх класів за кадром.

Тепер ви задаєтесь питанням, чому, напевно, це написано в нижньому регістрі, а не Type?

Ну, я думаю, це питання узгодженості str, клас, який створює рядки об'єктів, і intклас, який створює цілі об'єкти. type є тільки клас, який створює об'єкти класів.

Ви бачите це, перевіривши __class__ атрибут

Все, і я маю на увазі все, є об'єктом в Python. Що включає ints, рядки, функції та класи. Всі вони є об'єктами. І всі вони є створено з класу:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Тепер, що таке __class__ будь-якого __class__ ?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Отже, метаклас - це просто матеріал, який створює об'єкти класів.

Якщо хочете, ви можете назвати це "класовою фабрикою".

type це вбудований метаклас Python, але, звичайно, ви можете створити його власний метаклас

The __metaclass__ атрибут

Ви можете додати a __metaclass__ атрибут, коли ви пишете клас:

class Foo(object):
    __metaclass__ = something...
    [...]

Якщо ви це зробите, Python буде використовувати метаклас, щоб створити клас Foo.

Обережно, це складно.

Ви пишете class Foo(object) по-перше, але об'єкт класу Foo не створено в пам'яті ще.

Пітон шукатиме __metaclass__ в класі визначення. Якщо він це знайде він буде використовувати його для створення класу об'єктів Foo. Якщо ні, то він буде використовувати type створити клас.

Читайте це кілька разів.

Коли ви робите:

class Foo(Bar):
    pass

Python робить наступне:

Чи тут є __metaclass__ атрибут в Foo?

Якщо так, створіть у пам'яті об'єкт класу (я сказав об'єкт класу, залишитися зі мною тут), з ім'ям Foo використовуючи те, що є __metaclass__.

Якщо Python не може знайти __metaclass__, він буде шукати a __metaclass__ на рівні MODULE і спробуйте зробити те ж саме (але тільки для класів, які не успадковують нічого, в основному класи старого стилю).

Тоді, якщо його не знайти __metaclass__ зовсім, він буде використовувати Bar's (перший батько) власний метаклас (який може бути типовим type), щоб створити об'єкт класу.

Будьте обережні, щоб __metaclass__ атрибут не буде успадкований, метаклас батьків (Bar.__class__) буде. Якщо Bar використовується a __metaclass__ атрибут, який створений Bar з type() (і ні type.__new__()), підкласи не успадковують таку поведінку.

Тепер велике питання, що ви можете вкласти __metaclass__ ?

Відповідь: щось, що може створити клас.

І що може створити клас? type, або що-небудь, що підкласує або використовує його.

Користувальницькі метакласи

Основною метою метакласу є автоматичне змінення класу коли вона створена.

Зазвичай це робиться для API, де ви хочете створити класи, які відповідають поточний контекст.

Уявіть собі дурний приклад, де ви вирішите, що всі класи у вашому модулі повинні мати свої атрибути, написані великими літерами. Існує кілька способів Зробіть це, але один спосіб - встановити __metaclass__ на рівні модуля.

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

На щастя, __metaclass__ насправді може бути будь-який запитуваний, він не повинен бути a формальний клас (я знаю, щось з "класом" на його ім'я не повинно бути клас, іди цифра ... але це корисно).

Тому ми почнемо з простого прикладу, використовуючи функцію.

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

Тепер давайте зробимо точно так само, але використовуємо справжній клас для метакласу:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

Але це насправді не ООП. Ми називаємо type безпосередньо, і ми не переоцінюємо або зателефонуйте батькові __new__. Давай зробимо це:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

Ви, можливо, помітили додатковий аргумент upperattr_metaclass. є нічого особливого про це: __new__ завжди отримує клас, який він визначає, як перший параметр. Так само, як у вас є self для звичайних методів, які отримують екземпляр як перший параметр, або визначальний клас для методів класу.

Звичайно, назви, які я використовував тут, давно потребують ясності, але подобаються за self, всі аргументи мають звичайні імена. Так справжнє виробництво Метаклас буде виглядати так:

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

Ми можемо зробити його навіть більш чистим, використовуючи super, що полегшить успадкування (так, ви можете мати метакласи, успадковуючи від метакласів, наслідуючи тип):

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

Це воно. Насправді, більше немає про метакласи.

Причиною ускладнення коду за допомогою метакласів не тому з metaclasses, це тому, що ви зазвичай використовуєте метакласи, щоб робити кручений матеріал спираючись на самоаналіз, маніпулюючи спадщиною, vars наприклад __dict__і т. д.

Дійсно, метакласи особливо корисні для чорної магії, і тому складні речі. Але самі по собі вони просто:

  • перехоплює створення класу
  • змінити клас
  • повернути модифікований клас

Чому б ви використовували класи метакласів замість функцій?

З тих пір __metaclass__ може прийняти будь-яку запитувану, чому б ви використовували клас оскільки це, очевидно, складніше?

Для цього є кілька причин:

  • Намір ясно. Коли ти читаєш UpperAttrMetaclass(type), ти знаєш що буде слідувати
  • Ви можете використовувати OOP. Metaclass може успадковуватись від метакласу, перекривати батьківські методи. Метакласи навіть можуть використовувати метакласи.
  • Підкласи класу будуть екземплярами його метакласу, якщо ви вказали клас metaclass, але не з функцією metaclass.
  • Ви можете краще структурувати свій код. Ви ніколи не використовуєте метакласи для чогось тривіальний, як наведене вище приклад. Це зазвичай для чогось складного. Маючи Можливість зробити кілька методів і групувати їх в одному класі дуже корисна щоб полегшити читання коду.
  • Ви можете підключити __new__, __init__ і __call__. Що дозволить ти робиш різні речі. Навіть якщо зазвичай ви можете все це зробити __new__, деякі люди просто зручніше користуватися __init__.
  • Це називають метакласами, чорт це! Це має означати щось!

Чому б ви використали метакласи?

Тепер велике питання. Чому б ви використали деяку незрозумілу помилку?

Ну, звичайно ти не робиш:

Метакласи - більш глибока магія   99% користувачів ніколи не повинні турбуватися.   Якщо ви зацікавлені в тому,   ти не маєш (людей, які насправді   потрібно їм точно знати, що   вони їм потрібні, і вони не потрібні   пояснення чому).

Пітон Гуру Тім Петерс

Головним використанням для метакласу є створення API. Типовим прикладом цього є DJANGO ORM.

Це дозволяє визначити щось на зразок цього:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

Але якщо ви це зробите:

guy = Person(name='bob', age='35')
print(guy.age)

Це не поверне IntegerField об'єкт Це повернеться int, і навіть може взяти його безпосередньо з бази даних.

Це можливо тому, що models.Model визначає __metaclass__ і він використовує магію, яка перетворить її Person ви просто визначили з простими твердженнями в складний крюк до поля бази даних.

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

Останнє слово

По-перше, ви знаєте, що класи є об'єктами, які можуть створювати екземпляри.

Фактично, класи є самими прикладами. З метакласів.

>>> class Foo(object): pass
>>> id(Foo)
142630324

У Python все є об'єктом, і всі вони - або випадки класів або випадки метакласів.

Окрім type.

type це власне метаклас. Це не те, що ви могли б відтворювати в чистому Python, і це зроблено, обманюючи трохи при реалізації рівень

По-друге, метакласи ускладнюються. Можливо, ви не хочете їх використовувати дуже прості переклади класу. Ви можете змінювати класи за допомогою двох різних методів:

У 99% випадків, коли вам потрібна зміна класу, вам краще їх використовувати.

Але в 98% випадків ви взагалі не потребуєте класової переробки.


5763
2017-09-19 06:26



Здається, у Джанго models.Model вона не використовується __metaclass__ а скоріше class Model(metaclass=ModelBase): посилання на a ModelBase клас, який потім виконує вищезазначену метаклассу магію. Великий пост! Ось джерело Джанго: github.com/django/django/blob/master/django/db/models/... - Max Goodridge
<< Будьте обережні, щоб __metaclass__ атрибут не буде успадкований, метаклас батьків (Bar.__class__) буде. Якщо Bar використовується a __metaclass__ атрибут, який створений Bar з type() (і ні type.__new__()), підкласи не успадковуватимуть таку поведінку. >> - Не могли б ви / хтось пояснити трохи глибше цей уривок? - petrux
@MaxGoodridge Це синтаксис Python 3 для метакласів. Побачити Python 3.6 Модель даних В.С. Python 2.7 Модель даних - TBBle
Це відповідь на вікі в спільноті (таким чином, ті, хто прокоментував виправлення / вдосконалення, могли б розглянути можливість редагування своїх коментарів у відповідь, якщо вони впевнені, що вони правильні). - Shule
Які частини цієї відповіді стосуються python2 і який близько pythono3? - styrofoam fly


Зверніть увагу, ця відповідь на Python 2.x, як це було написано в 2008 році, метакласи трохи відрізняються в 3.x, див. Коментарі.

Метакласи - секретний соус, що робить "клас" роботу. Стандартний метаклас для нового об'єкта стилів називається "тип".

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

Метакласи приймають 3 аргументи. 'ім'я','підстави"і"диктувати'

Ось де починається секрет. Подивіться, де назву, бази та диктується в даному прикладі визначення класу.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

Давайте визначимо метаклас, який буде демонструвати, як 'клас:"це викликає".

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

І ось, приклад, який насправді означає щось, автоматично перетворить змінні в список "атрибути", встановлені в класі, і встановити на "Ні".

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

Зауважте, що магічне поведінка, яке "Initalized" отримує за рахунок метакласу init_attributes не передається в підклас Initalized.

Ось ще більш конкретний приклад, який показує, як можна підклас "набрати", щоб зробити метаклас, який виконує дію при створенні класу. Це досить складно:

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b

312
2017-09-19 06:45





Одне застосування для метакласів - додавання нових властивостей і методів до примірника автоматично.

Наприклад, якщо ви подивитеся Моделі Джанго, їх визначення виглядає трохи заплутаним. Схоже, що ви лише визначаєте властивості класу:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Проте в процесі виконання об'єкти Person заповнюються різними корисними методами. Див джерело для деяких дивовижних метакласів.


125
2018-06-21 16:30



Чи не використання мета-класів, додавши нові властивості та методи до a клас а не екземпляр? Наскільки я це зрозумів, мета-клас сам змінює клас, і в результаті екземпляри можуть бути змінені по різному. Може бути дещо помилковим для людей, які намагаються отримати природу мета-класу. Використання корисних методів на інстанціях може бути досягнуто за нормальних властивостей. Хоча посилання на кодекс Джанго є прикладом. - trixn


Інші пояснюють, як працюють метакласи та як вони вписуються в систему типу Python. Ось приклад того, як вони можуть бути використані. У тестовій базі, яку я написав, я хотів відслідковувати порядок, в якому класах було визначено, щоб я міг пізніше спробувати їх у такому порядку. Я виявив, що це найпростіше, використовуючи метаклас.

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

Все, що є підкласом MyType потім отримує атрибут класу _order що записує порядок визначення класів.


117
2017-09-19 06:32





Я думаю, що вступне слово ONLamp до програмування метакласу добре написане і дає дійсно гарне введення в цю тему, хоча вже кілька років тому.

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

Коротко кажучи: клас - це план створення екземпляра, метаклас є основою створення класу. Можна легко помітити, що в класах Python також повинні бути першокласні об'єкти, щоб включити цю поведінку.

Я сам ніколи не писав, але вважаю, що в одному з найбільш приємних застосувань метакласів можна побачити Джанго рамка. Модельні класи використовують підхід до метакласу, щоб забезпечити декларативний стиль написання нових моделей або класів форми. Поки метаклас створює клас, всі учасники отримують можливість самостійно налаштувати клас.

Що залишається сказати: якщо ви не знаєте, які метакласи, це ймовірність того, що ви вони не будуть потрібні становить 99%.


86
2017-08-10 23:28





Що таке метакласи? Що ви їх використовуєте?

TLDR: Метаклас екзаменує і визначає поведінку для класу, подібно до класу, що інстансує, і визначає поведінку для екземпляра.

Псевдокод:

>>> Class(...)
instance

Вище повинно виглядати знайомо. Ну, де це робиться Class прибув з? Це екземпляр метакласу (також псевдокод):

>>> Metaclass(...)
Class

У реальному коді ми можемо передати стандартний метаклас, type, все, що нам потрібно для показу класу, і ми отримуємо клас:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

Поставити це по-різному

  • Клас є екземпляром, оскільки метаклас є для класу.

    Коли ми створюємо об'єкт, ми отримуємо екземпляр:

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance
    

    Точно так само, коли явно визначаємо клас за допомогою метакласу за замовчуванням, type, ми показуємо його:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • Іншим способом, клас є прикладом метакласу:

    >>> isinstance(object, type)
    True
    
  • Третій шлях - метаклас - це клас класу.

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>
    

Коли ви пишете визначення класу, і Python виконує його, він використовує метаклас для інстанцірування об'єкта класу (який, у свою чергу, буде використаний для екземплярів екземплярів цього класу).

Точно так само, як ми можемо використовувати визначення класів, щоб змінити поведінку екземплярів користувацьких об'єктів, ми можемо використовувати визначення класу метакласу, щоб змінити спосіб, яким керує об'єкт класу.

Що вони можуть використовувати? Від документи:

Можливості використання метакласів безмежні. Деякі ідеї, які були вивчені, включають ведення журналу, перевірку інтерфейсу, автоматичну делегування, створення автоматичного ресурсу, проксі-сервери, рамки та автоматичне блокування / синхронізацію ресурсів.

Тим не менше, зазвичай рекомендується, щоб користувачі не використовували метакласи, якщо це не є абсолютно необхідним.

Ви використовуєте метаклас при кожному створенні класу:

Коли ви пишете визначення класу, наприклад, як це,

class Foo(object): 
    'demo'

Ви створюєте об'єкт класу.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

Це те ж саме, що функціонально викликає type з відповідними аргументами і присвоєння результату змінній цього імені:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

Зверніть увагу, що деякі речі автоматично додаються до __dict__, тобто простір імен:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

The метаклас об'єкта, який ми створили, в обох випадках є type.

(Примітка щодо вмісту класу __dict__: __module__ це тому, що класи повинні знати, де вони визначені, і __dict__ і __weakref__ є, тому що ми не визначаємо __slots__ - якщо ми визначити __slots__ ми будемо економити трохи місця в екземплярах, тому що ми можемо заборонити __dict__ і __weakref__ виключивши їх. Наприклад:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... але я відволікся.)

Ми можемо продовжити type як і будь-яке визначення іншого класу:

Ось за замовчуванням __repr__ класів:

>>> Foo
<class '__main__.Foo'>

Одне з найцінніших речей, які ми можемо зробити за замовчуванням при написанні об'єкта Python, - забезпечити його хорошим __repr__. Коли ми кличемо help(repr) ми дізнаємося, що існує хороший тест для a __repr__ що також вимагає тесту на рівність - obj == eval(repr(obj)). Наступне просте реалізація __repr__ і __eq__ для класових екземплярів класу нашого типу нам надається демонстрація, яка може покращитись за умовчанням __repr__ класів:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

Отже, коли ми створюємо об'єкт з цим метакласом, то __repr__ Відлуння в командному рядку забезпечує набагато менш потворний вигляд, ніж за замовчуванням:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

Приємно __repr__ визначено для екземпляру класу, ми маємо більш сильну здатність налагоджувати наш код. Однак значно далі перевіряти eval(repr(Class)) маловероятно (оскільки функціям було б досить неможливо оцінити їх за замовчуванням __repr__с)

Очікуване використання: __prepare__ простір імен

Якщо, наприклад, ми хочемо дізнатись, в якому порядку створюються методи класу, ми могли б забезпечити замовлений dict як простору імен класу. Ми би зробили це з __prepare__ котрий повертає простір імен для класу, якщо він реалізований у Python 3:

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

І використання:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

І тепер ми маємо запис того порядку, в якому ці методи (та інші атрибути класу) були створені:

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Зверніть увагу, цей приклад був адаптований з документація - новий enum у стандартній бібліотеці робить це.

Отже, що ми робили, ми створюємо клас із метакласу. Ми можемо також розглядати метаклас як будь-який інший клас. Він має спосіб вирішення методу:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

І це приблизно правильно repr (яку ми більше не можемо переглянути, якщо не знайдемо спосіб подання наших функцій):

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})

74
2018-03-01 19:48





Python 3 оновлення

Є (на даний момент) два ключові методи в метакласі:

  • __prepare__, і
  • __new__

__prepare__ дозволяє надати користувацьке відображення (наприклад, OrderedDict), який буде використовуватися як простір імен під час створення класу. Ви повинні повернути екземпляр будь-якого простору імен, який ви виберете. Якщо ти не реалізуєш __prepare__нормальний dict використовується.

__new__ несе відповідальність за фактичне створення / зміну останнього класу.

Голих кісток, без зайвих метакласків хотіли б:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

Простий приклад:

Скажімо, ви хочете, щоб простий код перевірки працював над вашими атрибутами - як завжди, це має бути int або a str. Без метакласу ваш клас виглядатиме так:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

Як видно, вам доведеться повторювати назву атрибута двічі. Це робить можливими помилки при дратуванні помилок.

Проста метаклассія може вирішити цю проблему:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

Це те, що буде виглядати метакласом (не використовуючи __prepare__ тому що це не потрібно):

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

Вибірка:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

виробляє:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

Примітка: Цей приклад достатньо простий, він також міг бути виконаний декоратором класу, але, мабуть, фактичний метаклас буде робити набагато більше.

Клас 'ValidateType' для довідки:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value

55
2017-10-13 09:21



У прикладі метакласу я отримую NameError: name 'ValidateType' is not defined. Будь-які пропозиції щодо того, як найкраще це виправити? Я використовую python 2 - Nickpick


Метаклас - це клас, який розповідає, як (якийсь) інший клас повинен бути створений.

Це той випадок, коли я бачив метаклас як рішення моєї проблеми: У мене була дуже складна проблема, яку, мабуть, можна було вирішити по-різному, але я вирішив це, використовуючи метаклас. Через складність, це один з небагатьох модулів, я написав, де коментарі в модулі перевищують кількість написаного коду. Ось...

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()

41
2017-08-09 18:49





Роль метакласу __call__() метод при створенні екземпляру класу

Якщо ви зробили програму Python більше, ніж через кілька місяців, то в кінцевому підсумку ти натрапиш на код, який виглядає так:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

Останнє можливе, коли ви реалізуєте __call__() магічний метод на класі.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

The __call__() метод викликається, коли екземпляр класу використовується як виклик. Але, як ми бачили з попередніх відповідей, сам клас є екземпляром метакласу, тому, коли ми використовуємо клас як виклик (тобто, коли ми створюємо його екземпляр), ми насправді називаємо його метакласом __call__() метод На даний момент більшість програмістів Python трохи заплутані, тому що їм було сказано, що при створенні такого примірника instance = SomeClass() ти називаєш це __init__() метод Деякі, хто викопав трохи глибше, знаю, що раніше __init__() там є __new__(). Ну, сьогодні, раніше, з'являється ще один рівень правди __new__() є метакласи __call__().

Давайте розглянемо ланцюжок викликів методу з конкретно перспективи створення екземпляра класу.

Це метаклас, який ґатує точно в той момент, коли екземпляр створюється, і момент, коли він буде повернути його.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

Це клас, який використовує цей метаклас

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

І тепер давайте створимо екземпляр з Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

Наведений вище код насправді не робить нічого іншого, окрім реєстрації завдання, а потім делегування фактичної роботи батькові (тобто збереження поведінки за умовчанням). Так з type буття Meta_1'ы батьківський клас, ми можемо уявити, що це буде псевдо-реалізація type.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

Ми бачимо, що це метаклас __call__() Метод є тим, який називається першим. Потім він делегує створення цього екземпляра у класі __new__()метод і ініціалізація до екземпляра __init__(). Це теж той, який остаточно повертає екземпляр.

З вищенаведеного випливає, що метаклас __call__() також надається можливість вирішити, чи потрібно дзвонити Class_1.__new__() або Class_1.__init__() в кінцевому підсумку буде зроблено. В ході виконання він може фактично повернути об'єкт, який не був зачеплений жодним із цих методів. Візьмемо, наприклад, такий підхід до єдиної моделі:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__() 
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

Давайте спостерігати, що відбувається, коли неодноразово намагаються створити об'єкт типу Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True

37
2017-12-27 02:21





type насправді є metaclass - клас, який створює інші класи. Більшість metaclass є підкласами Росії type. The metaclass отримує new class як свій перший аргумент і надайте доступ до об'єкта класу з деталями, як зазначено нижче:

>>> 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.


27
2017-07-13 07:58