Есть тонкие различия, в основном относящиеся к наследованию. При использовании функции в качестве метакласса результирующий класс действительно является экземпляром type
, и может быть унаследован без ограничений; однако функция метакласса никогда не будет вызвана для таких подклассов. При использовании подкласса type
в качестве метакласса результирующий класс будет экземпляром этого метакласса, как и любого из его подклассов; однако множественное наследование будет ограничено.
Иллюстрируя различия:
>>> def m1(name, bases, atts):
>>> print "m1 called for " + name
>>> return type(name, bases, atts)
>>>
>>> def m2(name, bases, atts):
>>> print "m2 called for " + name
>>> return type(name, bases, atts)
>>>
>>> class c1(object):
>>> __metaclass__ = m1
m1 called for c1
>>> type(c1)
<type 'type'>
>>> class sub1(c1):
>>> pass
>>> type(sub1)
<type 'type'>
>>> class c2(object):
>>> __metaclass__ = m2
m2 called for c2
>>> class sub2(c1, c2):
>>> pass
>>> type(sub2)
<type 'type'>
Обратите внимание, что при определении SUB1 и SUB2, никакие функции метакласса не назывались. Они будут созданы точно так, как если бы c1 и c2 не имели метаклассов, а вместо были обработаны после создания.
>>> class M1(type):
>>> def __new__(meta, name, bases, atts):
>>> print "M1 called for " + name
>>> return super(M1, meta).__new__(meta, name, bases, atts)
>>> class C1(object):
>>> __metaclass__ = M1
M1 called for C1
>>> type(C1)
<class '__main__.M1'>
>>> class Sub1(C1):
>>> pass
M1 called for Sub1
>>> type(Sub1)
<class '__main__.M1'>
Примечание различия уже: M1 называлась при создании SUB1, и оба классы являются экземплярами M1. Я использую super()
для фактического создания здесь, по причинам, которые станут понятны позже.
>>> class M2(type):
>>> def __new__(meta, name, bases, atts):
>>> print "M2 called for " + name
>>> return super(M2, meta).__new__(meta, name, bases, atts)
>>> class C2(object):
>>> __metaclass__ = M2
M2 called for C2
>>> type(C2)
<class '__main__.M2'>
>>> class Sub2(C1, C2):
>>> pass
M1 called for Sub2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 23, in __new__
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Это основное ограничение на множественное наследование с помощью метаклассов. Python не знает, совместимы ли M1 и M2 метаклассы, , поэтому он заставляет вас создать новый, чтобы гарантировать, что он делает то, что вам нужно.
>>> class M3(M1, M2):
>>> def __new__(meta, name, bases, atts):
>>> print "M3 called for " + name
>>> return super(M3, meta).__new__(meta, name, bases, atts)
>>> class C3(C1, C2):
>>> __metaclass__ = M3
M3 called for C3
M1 called for C3
M2 called for C3
>>> type(C3)
<class '__main__.M3'>
Вот почему я использовал super()
в метаклассе __new__
функции: так что каждый один может вызвать следующую в MRO.
Некоторые случаи использования, возможно, потребуется классы, чтобы быть типа type
, или может потребоваться , чтобы избежать проблем наследования, в этом случае функция метаклассом, вероятно путь. В других случаях тип класса может быть действительно важен, или вы можете использовать все подклассы, и в этом случае лучше было бы создавать подклассы type
. Не стесняйтесь использовать стиль, который лучше всего подходит для в любой ситуации.
Спасибо, что нашли время, чтобы объяснить это. –
Большое спасибо, редко я вижу такой ясный ответ. – Nikwin