Como cualquier otra forma de definición de clase anidada, una metaclass anidada puede ser más "compacta y conveniente" (siempre que acepte no reutilizar esa metaclase excepto por herencia) para muchos tipos de "uso de producción", pero puede ser algo inconveniente para la depuración y la introspección.
Básicamente, en lugar de dar la metaclase un nombre propio de nivel superior, que va a terminar con todas las metaclases personalizados definidos en un módulo que se está undistiguishable entre sí en base a sus atributos __module__
y __name__
(que es lo Python usa para formar su repr
si es necesario). Considere lo siguiente:
>>> class Mcl(type): pass
...
>>> class A: __metaclass__ = Mcl
...
>>> class B:
... class __metaclass__(type): pass
...
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>
OIA, si se desea examinar "qué tipo es la clase A" (una metaclase es el tipo de la clase, recuerda), se obtiene una respuesta clara y útil - es Mcl
en el módulo principal. Sin embargo, si se desea examinar "qué tipo es la clase B", la respuesta no es tan útil: dice es __metaclass__
en el módulo main
, pero eso no es cierto incluso:
>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>>
... allí es no existe tal cosa; esa repr es engañosa y no es muy útil ;-).
Una clase de repr es esencialmente '%s.%s' % (c.__module__, c.__name__)
- una regla simple, útil y consistente - pero en muchos casos como, la declaración class
no es única en el alcance del módulo, o no está en el alcance del módulo (sino dentro de una función o cuerpo de clase), o incluso no existente (las clases pueden construirse sin una declaración class
, llamando explícitamente a su metaclase), esto puede ser algo engañoso (y la mejor solución es evitar, en la medida de lo posible , esos casos peculiares, excepto cuando se puede obtener una ventaja sustancial al usarlos).Por ejemplo, considere:
>>> class A(object):
... def foo(self): print('first')
...
>>> x = A()
>>> class A(object):
... def foo(self): print('second')
...
>>> y = A()
>>> x.foo()
first
>>> y.foo()
second
>>> x.__class__
<class '__main__.A'>
>>> y.__class__
<class '__main__.A'>
>>> x.__class__ is y.__class__
False
con dos class
declaración en el mismo ámbito, el segundo vuelve a enlazar el nombre (aquí, A
), pero las instancias existentes se refieren a la primera unión del nombre por objeto, no por su nombre - Así que ambos objetos de clase permanecen, uno accesible solo a través del atributo type
(o __class__
) de sus instancias (si hay alguno - si ninguno, ese primer objeto desaparece) - las dos clases tienen el mismo nombre y módulo (y por lo tanto el misma representación), pero son objetos distintos. Las clases anidadas dentro de clases o cuerpos de funciones, o creadas llamando directamente a la metaclase (incluido type
), pueden causar una confusión similar si alguna vez se requiere depuración o introspección.
Por lo tanto, anidar la metaclase está bien si nunca tendrá que depurar o introspectivamente ese código, y puede ser vivido si quien lo hace comprende estas peculiaridades (aunque nunca será tan conveniente como usar un buen , nombre real, por supuesto, al igual que la depuración de una función codificada con lambda
no puede ser tan conveniente como la depuración codificada con def
). Por analogía con lambda
frente a def
puede afirmar razonablemente que la definición "anidada" anónima está bien para las metaclases que son tan absolutamente simples, tan ingenuas, que ninguna corrección de errores o introspección será posiblemente necesaria.
En Python 3, la "definición anidada" simplemente no funciona - no, una metaclase se debe pasar como un argumento de palabra clave a la clase, como en class A(metaclass=Mcl):
, por lo que define __metaclass__
en el cuerpo no tiene ningún efecto. Creo que esto también sugiere que una definición de metaclase anidada en el código de Python 2 probablemente solo sea apropiada si usted está seguro de que ese código nunca necesitará ser portado a Python 3 (ya que está haciendo que ese puerto sea mucho más difícil, y necesitará anular la definición de la metaclase para este fin) - código "desechable", en otras palabras, que no existirá en unos pocos años cuando alguna versión de Python 3 adquiera enormes ventajas de velocidad, funcionalidad o terceros. soporte partido, sobre Python 2.7 (la última versión de Python 2).
código que ha espera para ser desechable, como la historia de la informática nos muestra, tiene un hábito entrañable de sorprender que por completo, y siendo todavía unos 20 años más tarde (aunque tal vez el código que escribió en la misma época " para las edades "está completamente olvidado ;-). Esto ciertamente parece sugerir evitar la definición anidada de metaclases.
Muy buena explicación, gracias. – cji
Impresionante respuesta. Falta un poco en "vas a confiar ???, y serialización". –
No estoy seguro de cuánto de un problema es 'pickle'. El decapado parece funcionar bien [aquí] (http://gist.github.com/524744). –