Tôi đồng ý thừa kế là phù hợp hơn cho vấn đề đặt ra.
Tôi thấy câu hỏi này thực sự tiện dụng mặc dù trên các lớp trang trí, cảm ơn tất cả.
Dưới đây là một vài ví dụ khác, dựa trên các câu trả lời khác, bao gồm cách kế thừa ảnh hưởng đến mọi thứ trong Python 2.7, (và @wraps , duy trì chuỗi tài liệu gốc của hàm, v.v.):
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...
Thường thì bạn muốn thêm tham số vào trang trí của mình:
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()
Đầu ra:
@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
Tôi đã sử dụng một trình trang trí chức năng, vì tôi thấy chúng ngắn gọn hơn. Đây là một lớp để trang trí một lớp:
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
Một phiên bản mạnh mẽ hơn để kiểm tra các dấu ngoặc đơn đó và hoạt động nếu các phương thức không tồn tại trên lớp được trang trí:
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
Các assert
kiểm tra mà trang trí đã không được sử dụng mà không có dấu ngoặc đơn. Nếu nó có, thì lớp được trang trí được chuyển đến msg
tham số của trình trang trí, làm tăng một AssertionError
.
@decorate_if
chỉ áp dụng decorator
nếu condition
đánh giá True
.
Các getattr
, callable
kiểm tra, và @decorate_if
được sử dụng để trang trí các không phá vỡ nếu foo()
phương pháp không tồn tại trên lớp được trang trí.