Pregunta

Hoy me he encontrado una definición sorprendente de una metaclase en Python aquí , con la definición metaclase inline con eficacia. La parte pertinente es

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 tal definición en línea?

Otros argumentos:

Un argumento de una manera 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 utilizando metaclases está definiendo una metaclase y utilizarlo en una clase y luego inhertiting de eso. Por ejemplo, en una metaclase conservador la definición

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?

¿Fue útil?

Solución

Al igual que cualquier otra forma de definición de clase anidada, una metaclase anidada puede ser más "compacto y conveniente" (el tiempo que estás bien con no reutilizando que metaclase excepto por herencia) para muchos tipos de "uso en producción", pero puede ser un inconveniente tanto para la depuración y la introspección.

Básicamente, en lugar de dar la metaclase un nombre propio de nivel superior, usted va a terminar con todas las metaclases personalizados definidos en un módulo que se está undistiguishable entre sí en función de sus atributos y __module__ __name__ (que es lo que Python usos 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, recuerdo), 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 que de __metaclass__ en el módulo main, pero eso ni siquiera es cierto:

>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>> 

... hay es no hay tal cosa, en realidad; repr que es engañoso y no muy útiles; -).

repr de una clase es esencialmente '%s.%s' % (c.__module__, c.__name__) - un simple, útil y coherente regla - pero en muchos casos, tales como, la declaración class no ser único en el ámbito de módulo, o no estar en el alcance del módulo en absoluto (sino dentro un cuerpo de función o clase), o ni siquiera existente (clases supuesto, se pueden construir sin una declaración class, llamando explícitamente su metaclase), esto puede ser algo engañoso (y la mejor solución es evitar, en la medida de lo posible, aquellos casos peculiares, excepto cuando ventaja sustancial se puede obtener mediante el uso de ellos). 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 estados class 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 - por lo que ambos objetos de clase permanecen, uno accesible sólo a través de la type (o atributo __class__) de sus instancias (si los hay - en su defecto, que el primer objeto desaparece) - las dos clases tienen el mismo nombre y el módulo (y por lo tanto la misma representación), pero son distintos objetos. Clases anidadas dentro de los cuerpos de clase o función, o se crean llamando directamente a la metaclase (incluyendo type), puede causar confusión similar si la depuración o la introspección es cada vez llamado para.

Así, anidando la metaclase está bien si usted nunca tendrá que depurar o de lo contrario introspect ese código, y puede ser vivida con si el que está haciendo así que entender este peculiaridades (aunque nunca será tan conveniente como el uso de un bien, nombre real, por supuesto - al igual que la depuración de una función estará con lambda no puede posiblemente nunca sea tan conveniente como la depuración de uno estará con def). Por analogía con lambda vs def puede reclamar razonablemente que el anonimato, "anidada" definición está bien para metaclases que son tan absolutamente simples, tales obviedades, que alguna vez posible que se pidiera sin depurar o introspección.

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 metaclase anidado en el código Python 2 es probablemente apropiada sólo si se sabe con certeza que nunca necesitará ser portado a Python 3 (código ya que estás haciendo que el puerto mucho más difícil, y necesitará de-nido la definición metaclase con el propósito) - "usar y tirar" de código, es decir, que no van a estar en unos pocos años, cuando alguna versión de Python 3 adquiere enormes ventajas convincentes de velocidad, funcionalidad, o de tercera el apoyo del partido, over Python 2.7 (la última versión que haya de Python 2).

Código que esperas para ser desechable, como la historia de programas de computación nosotros, 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 torno al mismo tiempo "para las edades" está totalmente olvidado ;-). Sin duda, esto parece sugerir evitando definición anidada de metaclases.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top