decorator番外: 在python中尋求效率的人腦袋一定有問題
首發於微信公眾號: 硬核程式設計100式 (hardcore_100)
http://
weixin。qq。com/r/Ty3w6N-
E24KkrZuL93hd
(二維碼自動識別)
Python裡的decorator是個優雅的好東西呀! 它能保留原function主體邏輯, 避免一堆相關性比較小的程式碼汙染原function。
比如一個function需要對傳入變數做各種型別檢查, 在原function裡做一堆檢查就汙染了原本的邏輯了。
但是如果decorator使用過多, 或者在一個使用率非常高的基礎function上加decorator, 可能你會需要考慮一下效率問題。
最近我就遇到了這樣的問題, 並且糾結於程式碼可讀性和效率的平衡。 但腦子裡另一個聲音一直在吐槽, “你都用python了還考慮什麼效率啊喂! ”
太長不想看內容: TL;DR:
結論: decorator有overhead(額外開銷), 它的overhead在於多了一層function call(函式呼叫)。
實驗再次表明decorator不是魔法, 其實它就是多了一層function wrapper, 多了一個function call會導致原function速度慢一點點。
最後再思考一下, lambda會不會也有overhead?
同樣程式碼邏輯, 使用decorator會比較慢
結果:
func_without_decorator time: 3。4235661029815674
func_with_decorator time: 4。4265382289886475
程式碼:
def deco1(func):
def wrapper(x):
return x if isinstance(x,dict) else func(x)
return wrapper
def deco2(func):
def wrapper(x):
return x if isinstance(x,list) else func(x)
return wrapper
def deco3(func):
def wrapper(x):
return x if isinstance(x,float) else func(x)
return wrapper
def deco4(func):
def wrapper(x):
return x if isinstance(x,str) else func(x)
return wrapper
@deco4
@deco3
@deco2
@deco1
def func_with_decorator(x):
return x+x
def func_without_decorator(x):
if isinstance(x,dict):
return x
if isinstance(x,list):
return x
if isinstance(x,float):
return x
if isinstance(x,str):
return x
return x+x
import time
loops = 1000000
start = time。time()
for i in range(loops):
assert func_without_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_without_decorator time:”,end-start)
start = time。time()
for i in range(loops):
assert func_with_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_with_decorator time:”,end-start)
純粹新增decorator, 沒有任何程式碼邏輯, 速度也會慢
這個實驗測試了decorator的純overhead。
結果:
func_without_decorator time: 0。9140214920043945
func_with_decorator time: 2。0865042209625244
程式碼:
def deco1(func):
def wrapper(x):
return func(x)
return wrapper
def deco2(func):
def wrapper(x):
return func(x)
return wrapper
def deco3(func):
def wrapper(x):
return func(x)
return wrapper
def deco4(func):
def wrapper(x):
return func(x)
return wrapper
@deco4
@deco3
@deco2
@deco1
def func_with_decorator(x):
return x+x
def func_without_decorator(x):
return x+x
import time
loops = 1000000
start = time。time()
for i in range(loops):
assert func_without_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_without_decorator time:”,end-start)
start = time。time()
for i in range(loops):
assert func_with_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_with_decorator time:”,end-start)
使用decorator, 和同樣邏輯的額外function call, 效率等同
這個實驗證明了decorator的overhead在於function call。
結果:
func_without_decorator time: 4。68833327293396
func_with_decorator time: 4。719080924987793
程式碼:
def deco1(func):
def wrapper(x):
return x if isinstance(x,dict) else func(x)
return wrapper
def deco2(func):
def wrapper(x):
return x if isinstance(x,list) else func(x)
return wrapper
def deco3(func):
def wrapper(x):
return x if isinstance(x,float) else func(x)
return wrapper
def deco4(func):
def wrapper(x):
return x if isinstance(x,str) else func(x)
return wrapper
@deco4
@deco3
@deco2
@deco1
def func_with_decorator(x):
return x+x
def check1(x):
return isinstance(x,dict)
def check2(x):
return isinstance(x,list)
def check3(x):
return isinstance(x,float)
def check4(x):
return isinstance(x,str)
def func_without_decorator(x):
if check1(x):
return x
if check2(x):
return x
if check3(x):
return x
if check4(x):
return x
return x+x
import time
loops = 1000000
start = time。time()
for i in range(loops):
assert func_without_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_without_decorator time:”,end-start)
start = time。time()
for i in range(loops):
assert func_with_decorator(i)==i*2, “wut?”
end = time。time()
print(“func_with_decorator time:”,end-start)