2010-01-27 4 views
25

Я пытался понять метаклассы python, и поэтому прошел через образец кода. Насколько я понимаю, метакласс Python может быть любым вызываемым. Таким образом, я могу иметь мои метакласса какОбычные для наследования метаклассов?

def metacls(clsName, bases, atts): 
    .... 
    return type(clsName, bases, atts) 

Однако, я видел много людей пишут свои метаклассами следующим образом:

class Metacls(type): 
    def __new__(meta, clsName, bases, atts): 
     .... 
     return type.__new__(meta, clsName, bases, atts) 

Насколько я понимаю, это было бы и сделать тоже самое. Есть ли причина использовать базовый класс? Это обычное дело?

ответ

37

Есть тонкие различия, в основном относящиеся к наследованию. При использовании функции в качестве метакласса результирующий класс действительно является экземпляром 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. Не стесняйтесь использовать стиль, который лучше всего подходит для в любой ситуации.

+1

Спасибо, что нашли время, чтобы объяснить это. –

+1

Большое спасибо, редко я вижу такой ясный ответ. – Nikwin

Смежные вопросы