Поведение метакласса Python (без вызова __new__), есть ли объяснение?

В файле с именем exp.py (ниже) я пытаюсь понять метаклассы в Python. Похоже, что когда метод __new__ метакласса использует конструктор 'type' для создания класса, его метод __new__ не вызывается подклассом класса, использующего его в качестве метакласса:

class A(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        print
        return super(A, cls).__new__(cls, name, bases, dct)

class B(object):
    __metaclass__ = A

class C(B): pass

class D(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        return type(name, bases, dct)

class E(object):
    __metaclass__ = D

class F(E): pass

В терминале:

>>> from exp import *
cls is:    <class 'exp.A'>
name is:   B
bases is:  (<type 'object'>,)
dct is:    {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.A'>, '__init__': <function __init__ at 0x107eb9578>}

cls is:    <class 'exp.A'>
name is:   C
bases is:  (<class 'exp.B'>,)
dct is:    {'__module__': 'exp'}

cls is:    <class 'exp.D'>
name is:   E
bases is:  (<type 'object'>,)
dct is:    {'count': 0, '__module__': 'exp', '__metaclass__': <class 'exp.D'>, '__init__': <function __init__ at 0x107ebdb18>}
>>> 

Как видите, метод __new__ метакласса D не вызывается при загрузке определения класса F. (Если бы он был вызван, то была бы напечатана и информация о классе F.)

Может ли кто-нибудь помочь мне объяснить это?


Сообщение по теме: я читал Что такое метакласс в Python? и, кажется, встречает что-то похожее в предложении: «Будьте осторожны, здесь атрибут __metaclass__ не будет унаследован, метакласс родителя (Bar.__class__) будет унаследован. Если Bar использовал атрибут __metaclass__, который создал Bar с type() (а не type.__new__()), подклассы не будут наследовать это поведение». Но я не понял этого до конца.


person platypus    schedule 11.02.2015    source источник


Ответы (1)


Во втором случае вместо возврата экземпляра метакласса вы фактически возвращаете экземпляр type, что, в свою очередь, устанавливает атрибут __class__ вновь созданного класса E как <type 'type'> вместо D. Следовательно, согласно правилу 2 Python проверяет для атрибута __class__ базового класса (или type(E) или E.__class__) и решает использовать type в качестве метакласса F, поэтому __new__ D никогда не вызывается в этом случае.

  • Если dict['__metaclass__'] существует, он используется.
  • В противном случае, если есть хотя бы один базовый класс, используется его метакласс (сначала ищется атрибут __class__, а если он не найден, используется его тип).

Итак, правильный способ — вызвать метод __new__ типа в методе __new__ метакласса:

class D(type):
    def __new__(cls, name, bases, dct):
        print "cls is:   ", cls
        print "name is:  ", name
        print "bases is: ", bases
        print "dct is:   ", dct
        return type.__new__(cls, name, bases, dct)

class E(object):
    __metaclass__ = D

class F(E): pass

class A(object):
    pass

Вывод:

cls is:    <class '__main__.D'>
name is:   E
bases is:  (<type 'object'>,)
dct is:    {'__module__': '__main__', '__metaclass__': <class '__main__.D'>}
cls is:    <class '__main__.D'>
name is:   F
bases is:  (<class '__main__.E'>,)
dct is:    {'__module__': '__main__'}
person Ashwini Chaudhary    schedule 11.02.2015
comment
Отличный ответ спасибо! Кстати, у меня есть несколько вопросов по этой фразе (сначала ищет атрибут __class__, а если не находит, использует его тип). 1. В каких ситуациях атрибут __class__ не будет найден? 2. В каких ситуациях атрибут __class__ класса будет отличаться от его типа (определяемого с помощью type())? Спасибо - person platypus; 12.02.2015
comment
@platypus Классы в старом стиле не имеют этого атрибута, и даже их вывод для type() отличается. Для классов нового стиля по умолчанию используется тип type, а для классов старого стиля — classobj. Классические и новые классы - person Ashwini Chaudhary; 12.02.2015