裝飾器是一種巧妙簡潔的魔術,類似於Java中的面向切面編程,我們可以再函數執行前、執行后、拋出異常時做一些工作。利用裝飾器,我們可以抽象出一些共同的邏輯,簡化代碼。而簡化代碼的同時,就是在增加代碼魯棒性。
一、緩存
# coding:utf8
import time
import json
"""
簡單的內存緩存參數
"""
def simple_cache(timeout=3):
def decorator(f):
def ff(*args, **kwargs):
arg = json.dumps([args, kwargs])
res = None
key = f.__module__ + f.__name__ + arg
if hasattr(f, key):
res = getattr(f, key)
if time.time() - res['last_time'] > timeout:
res = None
if res is None:
res = {'last_time': time.time(), 'data': f(*args, **kwargs)}
setattr(f, key, res)
return res['data']
return ff
return decorator
if __name__ == '__main__':
@simple_cache(timeout=3)
def haha(user_id):
print("haha", user_id)
@simple_cache(timeout=3)
def baga(user_id):
print("baga", user_id)
haha(0)
baga(0)
haha(0)
haha(1)
time.sleep(5)
haha(1)
二、重試
在進行網絡請求時,我們常常需要重試幾次才能請求成功。這種套路經常使用,卻又嵌套的非常丑陋。此時,我們可以用裝飾器將重試邏輯抽象出來。
def retry(count=1):
def dec(f):
def ff(*args, **kwargs):
ex = None
for i in range(count):
try:
ans = f(*args, **kwargs)
return ans
except Exception as e:
ex = e
raise ex
return ff
return dec
db = []
@retry(count=10)
def until_six():
db.append("haha")
print("until_six")
return db[6]
print(until_six())
三、兼容舊版參數
庫都是隨着時間變化而不斷豐富的,函數的參數有可能發生變化,使用注解可以簡潔完美地兼容舊版本。
lib.py
def add(x, y):
return x + y
haha.py
from lib import add
z = add(x=4, y=5)
print(z)
現在庫函數add需要把x變成one,y變成two,同時需要保持haha.py正常運行。
lib.py
change_list = { # 存放函數名稱變化表,統一維護
'add': {
'x': 'one',
'y': 'two',
}
}
def legacy(f):
def ff(*args, **kwargs):
if f.__name__ in change_list:
for old_arg, new_arg in change_list[f.__name__].items():
if new_arg not in kwargs and old_arg in kwargs:
kwargs.update({new_arg: kwargs.get(old_arg)})
del kwargs[old_arg]
print(kwargs)
return f(**kwargs)
return ff
@legacy
def add(one, two, **kwargs):
return one + two
這種方式可以把API的全部變化放到一個統一的配置文件里面,當決定不再支持舊版時,直接從配置文件里面把舊版參數刪除掉即可。
四、deprecated
import logging
def deprecated(info):
def decorator(f):
def ff(*args, **kwargs):
logging.warning("%s is deprected. %s" % (f.__name__, info))
res = f(*args, **kwargs)
return res
return ff
return decorator