您當前的位置:首頁 > 歷史

decorator番外: 在python中尋求效率的人腦袋一定有問題

作者:由 minmin 發表于 歷史時間:2018-12-29

首發於微信公眾號: 硬核程式設計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番外: 在python中尋求效率的人腦袋一定有問題

同樣程式碼邏輯, 使用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)

標簽: func  return  TIME  decorator  def