2010-01-27 11 views
25

He estado tratando de entender las metaclases de python, y he estado examinando un código de ejemplo. Por lo que yo entiendo, una metaclase de Python puede ser invocable. Por lo tanto, puedo tener mis metaclase comoacostumbrado a heredar las metaclases del tipo?

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

Sin embargo, he visto mucha gente escribe sus metaclases de la siguiente manera:

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

Por lo que yo puedo ver, estos serían tanto hacer la misma cosa. ¿Hay alguna razón para usar la clase base en su lugar? ¿Es habitual?

Respuesta

37

Existen diferencias sutiles, en su mayoría relacionadas con la herencia. Cuando se usa una función como una metaclase, la clase resultante es realmente una instancia de type, y puede heredarse sin restricción; sin embargo, nunca se solicitará la función de metaclase para tales subclases. Cuando se utiliza una subclase de type como una metaclase , la clase resultante será una instancia de esa metaclase, al igual que cualquiera de sus subclases; sin embargo, la herencia múltiple estará restringida.

ilustrar las diferencias:

>>> 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'> 

Tenga en cuenta que la hora de definir SUB1 y SUB2, no hay funciones MetaClass fueron llamados. Se crearán exactamente como si c1 y c2 no tuvieran metaclases, pero en su lugar se manipularon después de la creación.

>>> 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'> 

observar las diferencias ya: M1 fue llamado al crear Sub 1, y ambos clases son instancias de M1. Estoy usando super() para la creación real aquí, por razones que se aclararán más adelante.

>>> 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 

Esta es la principal restricción en la herencia múltiple con metaclases. Python no sabe si M1 y M2 son metaclases compatibles, , por lo que te obliga a crear uno nuevo para garantizar que hace lo que necesitas.

>>> 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'> 

Esto es por lo que solía super() en la metaclase __new__ funciones: por lo que cada uno puede llamar al siguiente de la MRO.

Ciertos casos de uso pueden necesitar sus clases para ser de tipo type, o puede ser que desee para evitar los problemas de herencia, en cuyo caso una función metaclase es probablemente el camino a seguir. En otros casos, el tipo de la clase puede ser realmente importante, o es posible que desee operar en todas las subclases, en cuyo caso la subclase type sería una mejor idea. Siéntase libre de utilizar el estilo que mejor se adapte a en cualquier situación.

+1

Gracias por tomarse el tiempo para explicar eso. –

+1

Muchas gracias, es raro que vea una respuesta tan clara. – Nikwin

Cuestiones relacionadas