Вы спекулируете много, в то время как минималистский и «Специальный случаи» Python не являются достаточно специальными, чтобы нарушать правила ». директиву, легче понять, чем это.
В Python2 атрибут __metaclass__
в кубе класса используется во время создания класса, чтобы вызвать класс, который будет принадлежать этому классу. Обычно это класс с именем type
.Чтобы прояснить этот момент, после того, как парсер проанализировал тело класса, после компилятора он скомпилировал его в объект кода и после того, как он был фактически запущен во время выполнения программы, и только если в этом классе явно указано __metaclass__
.
Так способ проверить, что позволяет разрешить идет в таком случае, как:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A
имеет в своем теле __metaclass__
- MetaA
вызывается вместо type
, чтобы превратить его в «объект класса». B
не имеет __metaclass__
в своем теле. После его создания, если вы просто попытаетесь получить доступ к атрибуту __metaclass__
, это атрибут как любой другой, который будет виден, потому что Python выдает его из суперкласса A
. Если вы проверите A.__dict__
, вы увидите __metaclass__
, и если вы проверите B.__dict__
, не делайте этого.
Этот атрибут A.__metaclass__
является не используется вообще, когда B создан. Если вы измените его в A
до объявления B
, он по-прежнему будет использовать тот же метакласс, что и A
, потому что Python использует тип родительского класса в качестве метакласса в отсутствии объявления явного __metaclass__
.
Для иллюстрации:
In [1]: class M(type): pass
In [2]: class A(object): __metaclass__ = M
In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
In [4]: class B(A): pass
In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
In [6]: A.__metaclass__ = type
In [8]: class C(A): pass
In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
Кроме того, если вы пытаетесь просто создать класс с помощью вызова type
вместо того, чтобы использовать тело с class
заявлением, __metaclass__
также просто обычный атрибут:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
Подведение итогов: Атрибут __metaclass__
в Python 2 является особым, если он явно помещен в тело класса объявление как часть выполнения инструкции блока class
. Это обычный атрибут без каких-либо специальных свойств.
Python3 оба избавились от этого странного «атрибута __metaclass__
», и теперь для дальнейшей настройки тела класса разрешено изменять синтаксис, чтобы указать метаклассы. (Это как объявлено, как если бы он был «metaclass
именованный параметр» на самом class
заявление)
Теперь ко второй части того, что подняли свои сомнения: если в __new__
методе метакласса вы называете type
вместо type.__new__
, нет возможности, что Python может «знать» type
вызывается из производного метакласса. Когда вы вызываете type.__new__
, вы передаете в качестве своего первого параметра атрибут cls
, который сам metaclass __new__
был передан средой выполнения: это означает, что результирующий класс является экземпляром подкласса type
.Это так же, как наследование работает для любого другого класса в Python - так «никакого специальное поведения» здесь:
Таким образом, заметить разницу:
class M1(type):
def __new__(metacls, name, bases, attrs):
cls = type.__new__(metacls, name, bases, attrs)
# cls now is an instance of "M1"
...
return cls
class M2(type):
def __new__(metacls, name, bases, attrs):
cls = type(cls, name, bases, attrs)
# Type does not "know" it was called from within "M2"
# cls is an ordinary instance of "type"
...
return cls
Это можно увидеть в интерактивном режиме:
In [13]: class M2(type):
....: def __new__(metacls, name, bases, attrs):
....: return type(name, bases, attrs)
....:
In [14]: class A(M2): pass
In [15]: type(A)
Out[15]: type
In [16]: class A(M2): __metaclass__ = M2
In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
(Обратите внимание, что метаклассом __new__
метод первый параметр является сам метаклассом, поэтому более правильно назвали metacls
чем cls
как в вашем коде, и я na много кода «в дикой природе»)
Я думаю, что это связано с типом .__ new__ и type http://stackoverflow.com/questions/2608708/what-is-the-difference-between-type-and -типа новая-в-питон –