2012-04-28 12 views
5

He pasado por esto: What is a metaclass in Python?En Python, ¿cuándo debería usar una clase meta?

Pero, ¿alguien puede explicar más específicamente cuándo debo usar el concepto de metaclase y cuándo es muy útil?

Supongamos que tengo una clase, como a continuación:

class Book(object): 
    CATEGORIES = ['programming','literature','physics'] 
    def _get_book_name(self,book): 
     return book['title'] 
    def _get_category(self, book): 
     for cat in self.CATEGORIES: 
      if book['title'].find(cat) > -1: 
       return cat 
     return "Other" 


if __name__ == '__main__': 
    b = Book() 
    dummy_book = {'title':'Python Guide of Programming', 'status':'available'} 
    print b._get_category(dummy_book) 

Para esta clase.

¿En qué situación debo usar una metaclase y por qué es útil?

Gracias de antemano.

+12

La primera regla de las metaclases: si no * sabes * que necesitas una metaclase, * no * necesitas una metaclase. –

Respuesta

8

Usa metaclases cuando quiere mutar la clase mientras se está creando. Las metaclases casi nunca se necesitan, son difíciles de depurar y son difíciles de entender, pero ocasionalmente pueden hacer que los marcos sean más fáciles de usar. En nuestra base de código 600Kloc hemos utilizado metaclases 7 veces: ABCMeta una vez, 4x modelos. SubfieldBase de Django, y dos veces una metaclase que hace que las clases se puedan usar como vistas en Django. Como @Ignacio escribe, si no sabe sabe que necesita una metaclase (y ha considerado todas las demás opciones), no necesita necesita una metaclase.

+5

E incluso si quiere cambiar la clase, un decorador de clases es con frecuencia más sencillo y poderoso. Solo las manipulaciones más avanzadas se benefician de las metaclases. – delnan

1

Si por cualquier razón querer hacer cosas por el estilo Class[x], x in Class etc., usted tiene que utilizar metaclases:

class Meta(type): 
    def __getitem__(cls, x): 
     return x ** 2 

    def __contains__(cls, x): 
     return int(x ** (0.5)) == x ** 0.5 

# Python 2.x 
class Class(object): 
    __metaclass__ = Meta 

# Python 3.x 
class Class(metaclass=Meta): 
    pass 

print Class[2] 
print 4 in Class 
+3

* ¿Quieres *, pero es lo mejor que puedes hacer? Rara vez, si alguna vez. No puedo pensar en ningún momento cuando he querido hacer cosas como esta, pero incluso si lo hubiera hecho, sugeriría fuertemente que lo estaba haciendo de la manera incorrecta. –

3

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.

3

Se usa una metaclass siempre que necesite anular el comportamiento predeterminado para las clases, incluida su creación.

Una clase se crea a partir del nombre, una tupla de bases, y un diccionario de clase . Puede interceptar el proceso de creación para realizar cambios en cualquiera de esas entradas.

también puede anular cualquiera de los servicios prestados por las clases:

  • __call__ que se utiliza para crear instancias
  • __getattribute__ que se utiliza para buscar atributos y métodos de una clase
  • __setattr__ que controla establecimiento de atributos de
  • __repr__ que controla cómo se diplayed la clase

En resumen, las metaclases se usan cuando necesita controlar cómo se crean las clases o cuando necesita modificar cualquiera de los servicios proporcionados por las clases.

Cuestiones relacionadas