安裝
例子1
from lazyprop import lazyprop
class Foo(object):
def __init__(self):
self.load_count = 0
@lazyprop
def lazy(self):
self.load_count += 1
f = Foo()
f.lazy
f.lazy
f.lazy
print(f.load_count)
輸出:
1
例子2
from lazyprop import lazyprop
class Foo(object):
def __init__(self):
self.load_count = 0
# @lazyprop
def lazy(self):
self.load_count += 1
f = Foo()
f.lazy
f.lazy
f.lazy
print(f.load_count)
輸出:
0
補(bǔ)充:python語(yǔ)言中的AOP利器:裝飾器
一、前言
面向切面編程(AOP)是一種編程思想,與OOP并不矛盾,只是它們的關(guān)注點(diǎn)相同。面向?qū)ο蟮哪康脑谟诔橄蠛凸芾?,而面向切面的目的在于解耦和?fù)用。
舉兩個(gè)大家都接觸過(guò)的AOP的例子:
1)java中mybatis的@Transactional注解,大家知道被這個(gè)注解注釋的函數(shù)立即就能獲得DB的事務(wù)能力。
2)python中的with threading.Lock(),大家知道,被這個(gè)with代碼塊包裹的部分立即獲得同步的鎖機(jī)制。
這樣我們把事務(wù)和加鎖這兩種與業(yè)務(wù)無(wú)關(guān)的邏輯抽象出來(lái),在邏輯上解耦,并且可以輕松的做到代碼復(fù)用。
二、上下文管理器contextlib
當(dāng)然你可以使用with上下文管理器實(shí)現(xiàn)一些AOP的思想,這里有個(gè)模塊叫contextlib可以幫助你簡(jiǎn)易的實(shí)現(xiàn)上下文管理器。
上下文管理最常見(jiàn)的例子是with open('file') as fh,回收打開(kāi)句柄的例子。
這種方式還是比較麻煩的,下面我們看一下python中的裝飾器怎么樣實(shí)現(xiàn)AOP編程。
三、裝飾器:AOP的語(yǔ)法糖
python中的裝飾器就是設(shè)計(jì)來(lái)實(shí)現(xiàn)切面注入功能的。下面給出幾個(gè)例子,這幾個(gè)例子都是在生產(chǎn)環(huán)境驗(yàn)證過(guò)的。
其中的任務(wù)管理機(jī)是偽代碼,需要自己實(shí)現(xiàn)寫(xiě)數(shù)據(jù)庫(kù)的邏輯。
1、重試邏輯
只要do函數(shù)被@retry_exp裝飾,便可以獲得指數(shù)退避的重試能力。
@retry_exp(max_retries=10)
def do():
# do whatever
pass
那retry_exp是如何實(shí)現(xiàn)的呢?
def retry_exp(max_retries=3, max_wait_interval=10, period=1, rand=False):
def _retry(func):
def __retry(*args, **kwargs):
MAX_RETRIES = max_retries
MAX_WAIT_INTERVAL = max_wait_interval
PERIOD = period
RAND = rand
retries = 0
error = None
ok = False
while retries MAX_RETRIES:
try:
ret = func(*args, **kwargs)
ok = True
return ret
except Exception, ex:
error = ex
finally:
if not ok:
sleep_time = min(2 ** retries * PERIOD if not RAND else randint(0, 2 ** retries) * PERIOD, MAX_WAIT_INTERVAL)
time.sleep(sleep_time)
retries += 1
if retries == MAX_RETRIES:
if error:
raise error
else:
raise Exception("unknown")
return __retry
return _retry
2、降級(jí)開(kāi)關(guān)
只要do函數(shù)被@degrade裝飾,就會(huì)安裝app名稱校驗(yàn)redis里的開(kāi)關(guān),一旦發(fā)現(xiàn)開(kāi)關(guān)關(guān)閉,則do函數(shù)不被執(zhí)行,也就是降級(jí)。
@degrade
def do(app):
# do whatever
pass
那么degrade是怎樣實(shí)現(xiàn)的呢?
def degrade(app):
def _wrapper(function):
def __wrapper(*args, **kwargs):
value = None
try:
redis = codis_pool.get_connection()
value = redis.get("dmonitor:degrade:%s" % app)
except Exception, _:
logger.info(traceback.format_exc())
if not value or int(value) != 1:
function()
logger.info("[degrade] is_on: %s" % app)
else:
logger.info("[degrade] is_off: %s" % app)
return __wrapper
return _wrapper
3、任務(wù)狀態(tài)機(jī)
這個(gè)是最常用的,我們需要跟蹤落盤(pán)DB一個(gè)任務(wù)的執(zhí)行狀態(tài)(等待調(diào)度,執(zhí)行中,執(zhí)行成功,執(zhí)行失敗)
一旦do方法被@tasks_decorator裝飾,就獲得了這樣的能力。對(duì)item_param(是個(gè)json)中task_id指明的任務(wù)進(jìn)行狀態(tài)管理。
@tasks_decorator
def do(item_param):
# do whatever
pass
tasks_decorator是怎樣實(shí)現(xiàn)的呢?
def tasks_decorator(function):
def _wrap(*args, **kwargs):
param_dict = kwargs.get('item_param')
task_id = param_dict.get('task_id')
try:
param_dict.update({'status': TaskStatus.Waiting, 'start_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')})
try:
manager_dao.save_task(param_dict)
except Exception, ex:
pass
_update_task_status(task_id, TaskStatus.Doing)
function(*args, **kwargs)
_update_task_status(task_id, TaskStatus.Done)
except Exception as e:
time.sleep(0.5)
_update_task_status(task_id, TaskStatus.Fail, unicode(e.message))
raise
return _wrap
4、全局唯一性
在分布式+異步環(huán)境中,如果想保證exactly once是需要額外的邏輯的,其實(shí)主要是實(shí)現(xiàn)唯一鍵,一旦唯一鍵實(shí)現(xiàn)了,就可以使用公共緩存redis進(jìn)行唯一鍵判定了。
do函數(shù)被unique裝飾,那么對(duì)于task_id對(duì)應(yīng)的任務(wù),全局只會(huì)執(zhí)行一次。
@unique
def do(task_id):
# do whatever
pass
unique是怎樣實(shí)現(xiàn)的呢?
def unique(function):
def _wrap(*args, **kwargs):
task_id = kwargs.get('task_id')
try:
redis = codis_pool.get_connection()
key = "unique:%s" % task_id
if not redis.setnx(key):
redis.expire(key, 24*60*60)
function(*args, **kwargs)
except Exception as e:
logger.error(traceback.format_exc())
raise
return _wrap
四、總結(jié)
AOP在少量增加代碼復(fù)雜度的前提下,顯著的獲得以下優(yōu)點(diǎn):
1、使得功能邏輯和業(yè)務(wù)邏輯解耦,功能和業(yè)務(wù)的修改完全獨(dú)立,代碼結(jié)構(gòu)清晰,開(kāi)發(fā)方便
2、一鍵注入,代碼復(fù)用程度高,擴(kuò)展方便
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
您可能感興趣的文章:- 詳解Python裝飾器之@property
- python 裝飾器的使用與要點(diǎn)
- python高級(jí)語(yǔ)法之閉包和裝飾器詳解
- Python pytest裝飾器總結(jié)(實(shí)例詳解)
- Python裝飾器的應(yīng)用場(chǎng)景及實(shí)例用法
- python基礎(chǔ)之裝飾器詳解
- Python 的lru_cache裝飾器使用簡(jiǎn)介