2010-10-05 17 views
7

En comparación con los decoradores aplicados a una función, no es fácil entender los decoradores aplicados a una clase.Decoradores aplicados a la definición de clase con Python

@foo 
class Bar(object): 
    def __init__(self, x): 
     self.x = x 
    def spam(self): 
     statements 

¿Cuál es el caso de uso de decoradores para una clase? ¿Cómo usarlo?

Respuesta

23

Reemplaza la gran mayoría de los buenos usos clásicos para las metaclases personalizadas de una manera mucho más simple.

Piénselo de esta manera: nada que esté directamente en el cuerpo de la clase puede referirse al objeto de clase, porque el objeto de clase no existe hasta mucho después de que el cuerpo termine de ejecutarse (el trabajo de la metaclass es crear el objeto de clase - generalmente type, para todas las clases sin una metaclase personalizada).

embargo, el código en el decorador clase corre después se crea el objeto de clase (de hecho, con el objeto de clase como un argumento!) Y tan perfectamente puede también referirse a ese objeto de clase (y por lo general tiene que hacerlo)

Por ejemplo, considere:

def enum(cls): 
    names = getattr(cls, 'names', None) 
    if names is None: 
    raise TypeError('%r must have a class field `names` to be an `enum`!', 
        cls.__name__) 
    for i, n in enumerate(names): 
    setattr(cls, n, i) 
    return cls 

@enum 
class Color(object): 
    names = 'red green blue'.split() 

y ahora se puede hacer referencia a Color.red, Color.green, & C, en lugar de a 0, 1, etc. (Por supuesto que normalmente añadir aún más funcionalidad para hacer "un enum", pero aquí solo estoy mostrando la forma simple de agregar esa funcionalidad en un decorador de clases, en lugar de necesitar una metaclase personalizada! -)

+0

@Manoj, me alegro de que le haya gustado, tx por dejarme saber! –

6

Un caso de uso que puedo pensar es si usted quiere envolver todos los métodos de una clase con un decorador de funciones Digamos que tiene la siguiente decorador:

def logit(f): 
    def res(*args, **kwargs): 
     print "Calling %s" % f.__name__ 
     return f(*args, **kwargs) 
    return res 

Y la siguiente clase:

>>> class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
foo 
bar 
baz 

Se puede decorar todos los métodos:

>>> class Pointless: 
    @logit 
    def foo(self): print 'foo' 
    @logit 
    def bar(self): print 'bar' 
    @logit 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

Pero eso es cojo! En su lugar puede hacer esto:

>>> def logall(cls): 
for a in dir(cls): 
    if callable(getattr(cls, a)): 
     setattr(cls, a, logit(getattr(cls, a))) 
return cls 

>>> @logall 
class Pointless: 
    def foo(self): print 'foo' 
    def bar(self): print 'bar' 
    def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 

ACTUALIZACIÓN: Una versión más genérica de logall:

>>> def wrapall(method): 
    def dec(cls): 
     for a in dir(cls): 
      if callable(getattr(cls, a)): 
       setattr(cls, a, method(getattr(cls, a))) 
     return cls 
    return dec 

>>> @wrapall(logit) 
class Pointless: 
     def foo(self): print 'foo' 
     def bar(self): print 'bar' 
     def baz(self): print 'baz' 

>>> p = Pointless() 
>>> p.foo(); p.bar(); p.baz() 
Calling foo 
foo 
Calling bar 
bar 
Calling baz 
baz 
>>> 

divulgación: nunca he tenido que hacer esto y yo acaba de hacer este ejemplo arriba.

Cuestiones relacionadas