Ich würde zustimmen, dass Vererbung besser zu dem gestellten Problem passt.
Ich fand diese Frage allerdings sehr praktisch beim Dekorieren von Kursen, danke an alle.
Hier sind einige weitere Beispiele, die auf anderen Antworten basieren, einschließlich der Auswirkungen der Vererbung auf Python 2.7 (und @wraps , das die Dokumentzeichenfolge der ursprünglichen Funktion usw. beibehält):
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...
Oft möchten Sie Ihrem Dekorateur Parameter hinzufügen:
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()
Ausgänge:
@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
Ich habe einen Funktionsdekorateur verwendet, da ich sie prägnanter finde. Hier ist eine Klasse zum Dekorieren einer Klasse:
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
Eine robustere Version, die nach diesen Klammern sucht und funktioniert, wenn die Methoden für die dekorierte Klasse nicht vorhanden sind:
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
Die assert
Kontrollen , dass der Dekorateur nicht ohne Klammern verwendet. Wenn dies der Fall ist, wird die zu dekorierende Klasse an den msg
Parameter des Dekorators übergeben, wodurch ein ausgelöst wird AssertionError
.
@decorate_if
gilt nur decorator
wenn das condition
ausgewertet wird True
.
Die getattr
, callable
test und @decorate_if
werden verwendet, damit der Dekorator nicht kaputt geht, wenn die foo()
Methode für die zu dekorierende Klasse nicht vorhanden ist.