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.
Gracias por tomarse el tiempo para explicar eso. –
Muchas gracias, es raro que vea una respuesta tan clara. – Nikwin