## DEV Community

codemee

Posted on • Updated on

# Python 裝飾器 (Decorator)

## 裝飾器的緣起

``````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()
``````

## 裝飾器 (decorator) 語法

``````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:
....
``````

``````_decorator_func = decorator_here
func = _decorator_func(func)
``````

## 裝飾需要參數的函式

``````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

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)
``````

``````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()
``````

``````\$ 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')

``````