Người ta biết rằng hai đoạn mã sau gần như tương đương nhau:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
Một sai lầm phổ biến là nghĩ rằng @
chỉ cần che giấu đối số ngoài cùng bên trái.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
Sẽ dễ dàng hơn nhiều để viết trang trí nếu ở trên là cách @
làm việc. Thật không may, đó không phải là cách mọi thứ được thực hiện.
Hãy xem xét một trình trang trí Wait
giúp thực hiện chương trình trong vài giây. Nếu bạn không vượt qua trong thời gian chờ thì giá trị mặc định là 1 giây. Các trường hợp sử dụng được hiển thị dưới đây.
##################################################
@Wait
def print_something(something):
print(something)
##################################################
@Wait(3)
def print_something_else(something_else):
print(something_else)
##################################################
@Wait(delay=3)
def print_something_else(something_else):
print(something_else)
Khi Wait
có một đối số, chẳng hạn như @Wait(3)
, thì cuộc gọi Wait(3)
được thực hiện trước khi bất kỳ điều gì khác xảy ra.
Đó là, hai đoạn mã sau là tương đương
@Wait(3)
def print_something_else(something_else):
print(something_else)
###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
print(something_else)
Đây là một vấn đề.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
Một giải pháp được hiển thị dưới đây:
Hãy bắt đầu bằng cách tạo lớp sau DelayedDecorator
:
class DelayedDecorator:
def __init__(i, cls, *args, **kwargs):
print("Delayed Decorator __init__", cls, args, kwargs)
i._cls = cls
i._args = args
i._kwargs = kwargs
def __call__(i, func):
print("Delayed Decorator __call__", func)
if not (callable(func)):
import io
with io.StringIO() as ss:
print(
"If only one input, input must be callable",
"Instead, received:",
repr(func),
sep="\n",
file=ss
)
msg = ss.getvalue()
raise TypeError(msg)
return i._cls(func, *i._args, **i._kwargs)
Bây giờ chúng ta có thể viết những thứ như:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
Lưu ý rằng:
dec
không chấp nhận nhiều đối số.
dec
chỉ chấp nhận chức năng được bọc.
nhập lớp kiểm tra PolyArgDecoratorMeta (loại): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = tests. getmro (PolyArgDecoratorMeta) [1] r = SuperClass. gọi (Wait, args [0]) other: r = DelayedDecorator (Wait, * args, ** kwargs) other: r = DelayedDecorator (Wait, * args, ** kwargs) cuối cùng: pass return r
lớp thời gian nhập Chờ (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
Hai đoạn mã sau là tương đương:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
Chúng ta có thể in "something"
ra bàn điều khiển rất chậm, như sau:
print_something("something")
#################################################
@Wait(delay=1)
def print_something_else(something_else):
print(something_else)
##################################################
def print_something_else(something_else):
print(something_else)
dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)
##################################################
print_something_else("something")
Ghi chú cuối cùng
Nó có thể trông giống như rất nhiều mã, nhưng bạn không phải viết các lớp DelayedDecorator
và PolyArgDecoratorMeta
mọi lúc. Mã duy nhất bạn phải đích thân viết một cái gì đó như sau, khá ngắn:
from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(metaclass=PolyArgDecoratorMeta):
def __init__(i, func, delay = 2):
i._func = func
i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
execute_complete_reservation
mất hai tham số, nhưng bạn truyền nó một. Trang trí chỉ là đường cú pháp cho các chức năng gói bên trong các chức năng khác. Xem docs.python.org/reference/compound_stmts.html#feft để biết tài liệu đầy đủ.