2010-08-14 7 views
15

Hoy he encontrado una definición sorprendente de una metaclase en Python here, con la definición de metaclase efectivamente en línea. La parte relevante es¿Cuándo alinear las definiciones de metaclase en Python?

class Plugin(object): 
    class __metaclass__(type): 
     def __init__(cls, name, bases, dict): 
      type.__init__(name, bases, dict) 
      registry.append((name, cls)) 

¿Cuándo tiene sentido utilizar una definición en línea?

Otros argumentos:

Un argumento Una forma sería que la metaclase creado no es reutilizable en otro lugar utilizando esta técnica. Un argumento en contra es que un patrón común en el uso de metaclases es definir una metaclase y usarla en una clase y luego inhertitar desde allí. Por ejemplo, en la definición a conservative metaclass

class DeclarativeMeta(type): 
    def __new__(meta, class_name, bases, new_attrs): 
     cls = type.__new__(meta, class_name, bases, new_attrs) 
     cls.__classinit__.im_func(cls, new_attrs) 
     return cls 
class Declarative(object): 
    __metaclass__ = DeclarativeMeta 
    def __classinit__(cls, new_attrs): pass 

podría haber sido escrito como

class Declarative(object): #code not tested! 
    class __metaclass__(type): 
     def __new__(meta, class_name, bases, new_attrs): 
      cls = type.__new__(meta, class_name, bases, new_attrs) 
      cls.__classinit__.im_func(cls, new_attrs) 
      return cls 
    def __classinit__(cls, new_attrs): pass 

Cualquier otra consideración?

Respuesta

19

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.

+1

Muy buena explicación, gracias. – cji

+0

Impresionante respuesta. Falta un poco en "vas a confiar ???, y serialización". –

+0

No estoy seguro de cuánto de un problema es 'pickle'. El decapado parece funcionar bien [aquí] (http://gist.github.com/524744). –

Cuestiones relacionadas