2010-01-27 67 views
25

我一直在試圖理解python metaclasses,所以一直在經歷一些示例代碼。據我瞭解,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'> 

已經說明的差異:創建Sub1的時候M1叫,都 類是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