22
loading...
This website collects cookies to deliver better user experience
@
開頭的程式, 有些時候看起來很玄, 不過因為太多地方會用到這樣的語法, 因此即使想不去瞭解都不行, 以下就來認識一下所謂的『裝飾器』。def works():
total = 0
for i in range(10000):
total += i
print("total:", total)
works()
import time
def timing(func):
print("Start...")
t1 = time.perf_counter()
func()
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
def works():
total = 0
for i in range(10000):
total += i
print("total:", total)
timing(works)
import time
def timing(func):
def wrapper():
print("Start...")
t1 = time.perf_counter()
func()
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
return wrapper
def works():
total = 0
for i in range(10000):
total += i
print("total:", total)
works = timing(works)
works()
import time
def timing(func):
def wrapper():
print("Start...")
t1 = time.perf_counter()
func()
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
return wrapper
@timing
def works():
total = 0
for i in range(10000):
total += i
print("total:", total)
works()
@
開頭那一列的意思就是將 works 傳入 timing 後, 再將傳回的物件重新命名為 works, 也就等於前一個程式中的倒數第 2 列。你可以把 @timing
視為 加上 timing 功能的意思。利用這樣的語法, 完全不需要更動原本叫用 works 函式的程式, 就可以幫所有叫用 works 函式的程式計時了。@decorator_here
def func:
....
func = decorator_here(func)
decorator_here
必須是可叫用 (callable) 的物件, 並且符合必須傳入單一引數及傳回可叫用物件的規範, 而裝飾器就會把原始函式的名稱繫結到叫用此物件得到的傳回值。*args
和 **kwargs
來接收任意數量的參數, 再轉傳給被包裝的函式, 例如:import time
def timing(func):
def wrapper(*args, **kwargs):
print("Start...")
t1 = time.perf_counter()
func(*args, **kwargs)
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
return wrapper
@timing
def works(start, stop):
total = 0
for i in range(start, stop):
total += i
print("total:", total)
works(1, 10000)
import time
def timing(func):
def wrapper(*args, **kwargs):
print("Start...")
t1 = time.perf_counter()
res = func(*args, **kwargs)
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
return res
return wrapper
@timing
def works(start, stop):
total = 0
for i in range(start, stop):
total += i
return total
print("total:", works(1, 10000))
import time
def timing(times):
def outer(func):
def wrapper():
print("Start...")
t1 = time.perf_counter()
for i in range(times):
func()
t2 = time.perf_counter()
print("Elapsed time(secs):", t2 - t1)
return wrapper
return outer
@timing(2)
def works():
print("running...")
total = 0
for i in range(10000):
total += i
print("total:", works())
works = timing(2)(works)
timing(2)
會傳回 outer
函式, 而 outer
函式符合必須傳入單一引數及傳回可叫用物件的規範, 因此上述裝飾器語法的等效程式就可以正確運作。outer
時, 會將隨 timing(2)
傳入 2 所繫結的 times
名稱納入閉包 (closure), 因此後續叫用 outer
時就仍然可以透過 times
取得指定的執行次數, 據此傳回最後的 wrapper
函式。def deco_multiple():
def outmost():
def outer(func):
def wrapper():
print('start')
func()
print('done')
return wrapper
return outer
return outmost
@deco_multiple()()
def works():
print('working.')
works()
deco_multiple()
會傳回 outmost
, 因此 deco_multiple()()
就等於 outmost()
會傳回 outer
, 而 outer
符合必須傳入單一引數及傳回可叫用物件的規範, 但是裝飾器語法的規格就是 @ 後面只能出現一次函式叫用, 因此上面的程式執行時會出現語法錯誤:$ py .\deco_multi_test.py
File "dec.py", line 12
@deco_multiple()()
^
SyntaxError: invalid syntax
$ py .\deco_multi_test.py
start
working.
done
class EventHandler:
def __init__(self):
self.handlers = {}
def on(self, ev):
def wrapper(func):
self.handlers[ev] = func
return func
return wrapper
def dispatch(self, ev):
if ev in self.handlers:
self.handlers[ev]()
e = EventHandler()
@e.on('click')
def onClick():
print('clicked')
@e.on('double_click')
def onDoubleClick():
print('double clicked')
e.dispatch('click')
e.dispatch('double_click')
$ python .\deco_event.py
clicked
double clicked
class EventHandler:
def __init__(self):
self.handlers = {}
def on(self, ev, func = None):
if func:
self.handlers[ev] = func
else:
def wrapper(func):
self.handlers[ev] = func
return func
return wrapper
def dispatch(self, ev):
if ev in self.handlers:
self.handlers[ev]()
e = EventHandler()
@e.on('click')
def onClick():
print('clicked')
def onDoubleClick():
print('double clicked')
e.on('double_click', onDoubleClick)
e.dispatch('click')
e.dispatch('double_click')
on()
時沒有傳遞函式給 func 參數, 因此會執行 else
的部分, 也就是原本裝飾器的實作內容;但若直接叫用 on()
時就可以同時傳遞事件處理函式, 兩種語法並存, 都可以達到相同的效果。