Tôi biết câu hỏi này đã cũ, nhưng một số nhận xét là mới, và trong khi tất cả các giải pháp khả thi về cơ bản đều giống nhau, hầu hết chúng đều không rõ ràng hoặc dễ đọc.
Giống như câu trả lời của thobe đã nói, cách duy nhất để xử lý cả hai trường hợp là kiểm tra cả hai tình huống. Cách đơn giản nhất là kiểm tra xem có một đối số duy nhất và nó có phải là callabe hay không (LƯU Ý: sẽ cần kiểm tra thêm nếu trình trang trí của bạn chỉ nhận 1 đối số và nó là một đối tượng có thể gọi):
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
else:
Trong trường hợp đầu tiên, bạn làm những gì mà bất kỳ trình trang trí bình thường nào làm, trả về phiên bản đã sửa đổi hoặc gói của hàm được truyền vào.
Trong trường hợp thứ hai, bạn trả về một trình trang trí 'mới' bằng cách nào đó sử dụng thông tin được chuyển vào bằng * args, ** kwargs.
Điều này là tốt và tất cả, nhưng phải viết nó ra cho mọi trang trí bạn thực hiện có thể khá khó chịu và không sạch sẽ. Thay vào đó, sẽ rất tuyệt nếu có thể tự động sửa đổi trình trang trí của chúng tôi mà không cần phải viết lại chúng ... nhưng đó là những gì người trang trí dành cho!
Sử dụng trình trang trí decorator sau đây, chúng ta có thể phân bổ trình trang trí của mình để chúng có thể được sử dụng có hoặc không có đối số:
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
return f(args[0])
else:
return lambda realf: f(realf, *args, **kwargs)
return new_dec
Bây giờ, chúng ta có thể trang trí các trình trang trí của mình bằng @doublewrap và chúng sẽ hoạt động có và không có đối số, với một lưu ý:
Tôi đã lưu ý ở trên nhưng nên lặp lại ở đây, việc kiểm tra trong trình trang trí này tạo ra một giả định về các đối số mà trình trang trí có thể nhận (cụ thể là nó không thể nhận một đối số duy nhất, có thể gọi). Vì chúng tôi hiện đang làm cho nó có thể áp dụng cho bất kỳ máy phát điện nào, nó cần được ghi nhớ hoặc sửa đổi nếu nó có mâu thuẫn.
Những điều sau đây thể hiện công dụng của nó:
def test_doublewrap():
from util import doublewrap
from functools import wraps
@doublewrap
def mult(f, factor=2):
'''multiply a function's return value'''
@wraps(f)
def wrap(*args, **kwargs):
return factor*f(*args,**kwargs)
return wrap
@mult
def f(x, y):
return x + y
@mult(3)
def f2(x, y):
return x*y
@mult(factor=5)
def f3(x, y):
return x - y
assert f(2,3) == 10
assert f2(2,5) == 30
assert f3(8,1) == 5*7
@redirect_output
là không nhiều thông tin. Tôi cho rằng đó là một ý tưởng tồi. Sử dụng mẫu đầu tiên và đơn giản hóa cuộc sống của bạn rất nhiều.