Я погоджуюся, що спадщина краще підходить для поставленої проблеми.
Мені це питання було дуже зручним, хоча на уроках прикраси, дякую всім.
Ось ще пара прикладів, заснованих на інших відповідях, зокрема про те, як успадкування впливає на речі в Python 2.7, (та @wraps , що підтримує докстринг оригінальної функції тощо):
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...
Часто ви хочете додати параметри декоратора:
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()
Виходи:
@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
Я використовував функціональний декоратор, оскільки вважаю їх більш стислими. Ось клас для прикраси класу:
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
Більш надійна версія, яка перевіряє ці круглі дужки та працює, якщо методів немає в оформленому класі:
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
The assert
перевіряє , що декоратор не використовується без дужок. Якщо він є, то клас, який декорується, передається msg
параметру декоратора, який піднімає AssertionError
.
@decorate_if
застосовується лише decorator
якщо condition
оцінює до True
.
Тест getattr
, callable
тест і @decorate_if
використовуються так, щоб декоратор не зламався, якщо foo()
метод не існує для класу, який декорується.