Conceptualmente, existe una clase para definir lo que es un conjunto de objetos (las instancias de la clase) tienen en común. Eso es todo. Le permite pensar en las instancias de la clase de acuerdo con ese patrón compartido definido por la clase. Si cada objeto fuera diferente, no nos molestaríamos en usar clases, solo usaríamos diccionarios.
Una metaclase es una clase ordinaria, y existe por el mismo motivo; para definir lo que es común a sus instancias. La metaclase predeterminado type
ofrece todas las reglas normales que hacen clases e instancias funcionan de la manera que estamos acostumbrados, tales como:
- búsqueda de atributos en una instancia comprueba la instancia seguida de su clase, seguido de todos los super-clases para MRO
- Calling
MyClass(*args, **kwargs)
invoca i = MyClass.__new__(MyClass, *args, **kwargs)
para obtener una instancia, a continuación, invoca i.__init__(*args, **kwargs)
para inicializar que
- una clase se crea a partir de las definiciones en un bloque de clase haciendo que todos los nombres de la envolvente en el bloque de clase en los atributos de la clase
- Etc
Si usted quiere tener algunas clases que funcionan de manera diferente a las clases normales, se puede definir una metaclase y hacer sus clases inusuales instancias de la metaclase en lugar de type
. Es casi seguro que su metaclase sea una subclase de type
, porque probablemente no desee que su clase diferente de clase sea completamente diferente a; del mismo modo que puede desear que algunos subconjuntos de Libros se comporten de forma un tanto diferente (por ejemplo, libros que son compilaciones de otros trabajos) y usen una subclase de Book
en lugar de una clase completamente diferente.
Si no está tratando de definir una forma de hacer que algunas clases funcionen de manera diferente a las clases normales, entonces una metaclase probablemente no sea la solución más adecuada. Tenga en cuenta que las "clases definen cómo funcionan sus instancias" es ya un paradigma muy flexible y abstracto; la mayoría de las veces no necesita cambiar cómo funcionan las clases.
Si busca en Google, verá muchos ejemplos de metaclases que en realidad solo se usan para hacer un montón de cosas en torno a la creación de clases; a menudo procesando automáticamente los atributos de la clase, o encontrando nuevos automáticamente desde algún lugar. Realmente no llamaría esos grandes usos de las metaclases. No están cambiando la forma en que funcionan las clases, solo están procesando algunas clases. Una función de fábrica para crear las clases, o un método de clase que invoque inmediatamente después de la creación de clases, o lo mejor de todo, un decorador de clases, sería una mejor manera de implementar este tipo de cosas, en mi opinión.
Pero ocasionalmente se encuentra escribiendo código complejo para obtener el comportamiento predeterminado de Python de las clases para hacer algo conceptualmente simple, y realmente ayuda a dar un paso "más allá" e implementarlo en el nivel de metaclase.
Un ejemplo bastante trivial es el "patrón singleton", donde tiene una clase de la que solo puede haber una instancia; llamar a la clase devolverá una instancia existente si ya se ha creado una. Personalmente, estoy en contra de los singleton y no aconsejaría su uso (creo que son solo variables globales, astutamente disfrazadas para parecerse a las instancias recientemente creadas con el fin de ser aún más propensas a causar errores sutiles). Pero la gente los usa, y hay un gran número de recetas para hacer clases de singleton usando __new__
y __init__
. Hacerlo de esta manera puede ser un poco irritante, principalmente porque Python quiere llamar al __new__
y luego llamar al __init__
sobre el resultado de eso, por lo que debe encontrar la manera de no volver a ejecutar el código de inicialización cada vez que alguien solicite acceso al semifallo. Pero no sería más fácil si pudiéramos decirle directamente a Python qué queremos que suceda cuando llamemos a la clase, en lugar de tratar de configurar las cosas que Python quiere hacer para que, al final, hagan lo que queremos.
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
Menos de 10 líneas, y resulta clases normales en embarazos únicos simplemente añadiendo __metaclass__ = Singleton
, es decir nada más que una declaración de que son un producto único. Es solo más fácil implementar este tipo de cosas en este nivel, que hacer algo directamente en el nivel de clase.
Pero para su clase específica Book
, no parece que tenga que hacer nada que pueda ser ayudado por una metaclase. Realmente no necesita alcanzar metaclases a menos que encuentre que las reglas normales de cómo funcionan las clases le impiden hacer algo que debería ser simple de una manera simple (que es diferente de "hombre, ojalá no lo hice"). Tengo que escribir tanto para todas estas clases, me pregunto si podría generar automáticamente los bits comunes "). De hecho, nunca he usado una metaclase para algo real, a pesar de usar Python todos los días en el trabajo; todas mis metaclases han sido ejemplos de juguetes como el anterior Singleton
o simplemente una exploración tonta.
La primera regla de las metaclases: si no * sabes * que necesitas una metaclase, * no * necesitas una metaclase. –