Estoy de acuerdo en que la herencia es una mejor opción para el problema planteado.
Encontré esta pregunta realmente útil en las clases de decoración, gracias a todos. Aquí hay otro par de ejemplos, sobre la base de otras respuestas, incluyendo cómo afecta la herencia cosas en Python 2.7, (y @wraps, que mantiene cadena de documentación de la función original, etc):
def dec(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
@dec # no parentheses
class Foo...
frecuencia con que desea añadir parámetros a su decorador:
from functools import wraps
def dec(msg='default'):
def decorator(klass):
old_foo = klass.foo
@wraps(klass.foo)
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
@dec('foo decorator') # you must add parentheses now, even if they're empty
class Foo(object):
def foo(self, *args, **kwargs):
print('foo.foo()')
@dec('subfoo decorator')
class SubFoo(Foo):
def foo(self, *args, **kwargs):
print('subfoo.foo() pre')
super(SubFoo, self).foo(*args, **kwargs)
print('subfoo.foo() post')
@dec('subsubfoo decorator')
class SubSubFoo(SubFoo):
def foo(self, *args, **kwargs):
print('subsubfoo.foo() pre')
super(SubSubFoo, self).foo(*args, **kwargs)
print('subsubfoo.foo() post')
SubSubFoo().foo()
Salidas:
@decorator pre subsubfoo decorator
subsubfoo.foo() pre
@decorator pre subfoo decorator
subfoo.foo() pre
@decorator pre foo decorator
foo.foo()
@decorator post foo decorator
subfoo.foo() post
@decorator post subfoo decorator
subsubfoo.foo() post
@decorator post subsubfoo decorator
he utilizado un decorador función, ya que me parece más concisos. Aquí está una clase para decorar una clase:
class Dec(object):
def __init__(self, msg):
self.msg = msg
def __call__(self, klass):
old_foo = klass.foo
msg = self.msg
def decorated_foo(self, *args, **kwargs):
print('@decorator pre %s' % msg)
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
Una versión más robusta que los controles de los paréntesis, y funciona si no existen los métodos de la clase decorada:
from inspect import isclass
def decorate_if(condition, decorator):
return decorator if condition else lambda x: x
def dec(msg):
# Only use if your decorator's first parameter is never a class
assert not isclass(msg)
def decorator(klass):
old_foo = getattr(klass, 'foo', None)
@decorate_if(old_foo, wraps(klass.foo))
def decorated_foo(self, *args ,**kwargs):
print('@decorator pre %s' % msg)
if callable(old_foo):
old_foo(self, *args, **kwargs)
print('@decorator post %s' % msg)
klass.foo = decorated_foo
return klass
return decorator
Los assert
cheques que el decorador no se ha usado sin paréntesis.Si lo tiene, la clase que se está decorando se pasa al parámetro msg
del decorador, lo que genera un AssertionError
.
@decorate_if
solo aplica decorator
si condition
evalúa a True
.
El getattr
, callable
prueba, y @decorate_if
se utilizan para que el decorador no se rompe si el método foo()
no existe en la clase de ser decorado.
Si bien es útil, los decoradores de Python son diferentes al patrón de decorador. Un desafortunado nombre, supongo. Aunque puedo terminar yendo de esa manera. Gracias. –
@Robert Gowland: Muy diplomático. –