เป็นที่ทราบกันดีว่าโค้ดสองชิ้นต่อไปนี้เกือบเท่ากัน:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
ข้อผิดพลาดทั่วไปคือการคิดว่า@
ซ่อนอาร์กิวเมนต์ที่เหลือไว้
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
การเขียนมัณฑนากรจะง่ายกว่านี้ถ้าการ@
ทำงานเป็นอย่างไร น่าเสียดายที่นั่นไม่ใช่สิ่งที่ทำ
พิจารณามัณฑนากรWait
ซึ่งหลอกหลอนการทำงานของโปรแกรมสักสองสามวินาที หากคุณไม่ผ่านในช่วงเวลารอค่าเริ่มต้นคือ 1 วินาที กรณีใช้งานแสดงอยู่ด้านล่าง
##################################################
@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)
เมื่อWait
มีข้อโต้แย้งเช่น@Wait(3)
จากนั้นสายWait(3)
จะถูกดำเนินการก่อนสิ่งอื่นใดเกิดขึ้น
นั่นคือโค้ดสองชิ้นต่อไปนี้เทียบเท่ากัน
@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)
นี่คือปัญหา.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
วิธีแก้ไขปัญหาหนึ่งแสดงอยู่ด้านล่าง:
ให้เราเริ่มต้นด้วยการสร้างคลาสต่อไปนี้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)
ตอนนี้เราสามารถเขียนสิ่งต่าง ๆ เช่น:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
โปรดทราบว่า:
dec
ไม่ยอมรับข้อโต้แย้งหลายข้อ
dec
ยอมรับฟังก์ชั่นที่จะถูกห่อเท่านั้น
อิมพอร์ตตรวจสอบคลาส PolyArgDecoratorMeta (ชนิด): การเรียก def (Wait, * args, ** kwargs): ลอง: arg_count = len (args) ถ้า (arg_count == 1): ถ้า callable (args [0]): SuperClass = ตรวจสอบ getmro (PolyArgDecoratorMeta) [1] r = SuperClass โทร (รอ args [0]) อื่น: r = DelayedDecorator (รอ * args, ** kwargs) อื่น: r = DelayedDecorator (รอ args * args, ** kwargs) ในที่สุด: pass return r
อิมพอร์ตคลาสเวลารอ (metaclass = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = ความล่าช้า
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
โค้ดสองชิ้นต่อไปนี้เทียบเท่ากัน:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
เราสามารถพิมพ์"something"
ไปยังคอนโซลได้ช้ามากดังนี้:
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")
หมายเหตุสุดท้าย
มันอาจดูเหมือนโค้ดจำนวนมาก แต่คุณไม่จำเป็นต้องเขียนคลาสDelayedDecorator
และPolyArgDecoratorMeta
ทุกครั้ง รหัสเดียวที่คุณต้องเขียนด้วยตนเองดังนี้: ซึ่งค่อนข้างสั้น:
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
รับพารามิเตอร์สองตัว แต่คุณจะผ่านมันไป นักตกแต่งเป็นเพียงน้ำตาลซินแทคติกสำหรับห่อฟังก์ชั่นภายในฟังก์ชั่นอื่น ๆ ดูdocs.python.org/reference/compound_stmts.html#functionสำหรับเอกสารฉบับสมบูรณ์