Python 中@lazyprop 裝飾器的用法
安裝
pip install lazyprop
例子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
補充:python語言中的AOP利器:裝飾器
一、前言
面向切面編程(AOP)是一種編程思想,與OOP并不矛盾,只是它們的關(guān)注點相同。面向?qū)ο蟮哪康脑谟诔橄蠛凸芾?,而面向切面的目的在于解耦和?fù)用。
舉兩個大家都接觸過的AOP的例子:
1)java中mybatis的@Transactional注解,大家知道被這個注解注釋的函數(shù)立即就能獲得DB的事務(wù)能力。
2)python中的with threading.Lock(),大家知道,被這個with代碼塊包裹的部分立即獲得同步的鎖機制。
這樣我們把事務(wù)和加鎖這兩種與業(yè)務(wù)無關(guān)的邏輯抽象出來,在邏輯上解耦,并且可以輕松的做到代碼復(fù)用。
二、上下文管理器contextlib
當(dāng)然你可以使用with上下文管理器實現(xiàn)一些AOP的思想,這里有個模塊叫contextlib可以幫助你簡易的實現(xiàn)上下文管理器。
上下文管理最常見的例子是with open('file') as fh,回收打開句柄的例子。
這種方式還是比較麻煩的,下面我們看一下python中的裝飾器怎么樣實現(xiàn)AOP編程。
三、裝飾器:AOP的語法糖
python中的裝飾器就是設(shè)計來實現(xiàn)切面注入功能的。下面給出幾個例子,這幾個例子都是在生產(chǎn)環(huán)境驗證過的。
其中的任務(wù)管理機是偽代碼,需要自己實現(xiàn)寫數(shù)據(jù)庫的邏輯。
1、重試邏輯
只要do函數(shù)被@retry_exp裝飾,便可以獲得指數(shù)退避的重試能力。
@retry_exp(max_retries=10) def do(): # do whatever pass
那retry_exp是如何實現(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、降級開關(guān)
只要do函數(shù)被@degrade裝飾,就會安裝app名稱校驗redis里的開關(guān),一旦發(fā)現(xiàn)開關(guān)關(guān)閉,則do函數(shù)不被執(zhí)行,也就是降級。
@degrade def do(app): # do whatever pass
那么degrade是怎樣實現(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)機
這個是最常用的,我們需要跟蹤落盤DB一個任務(wù)的執(zhí)行狀態(tài)(等待調(diào)度,執(zhí)行中,執(zhí)行成功,執(zhí)行失敗)
一旦do方法被@tasks_decorator裝飾,就獲得了這樣的能力。對item_param(是個json)中task_id指明的任務(wù)進行狀態(tài)管理。
@tasks_decorator def do(item_param): # do whatever pass
tasks_decorator是怎樣實現(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是需要額外的邏輯的,其實主要是實現(xiàn)唯一鍵,一旦唯一鍵實現(xiàn)了,就可以使用公共緩存redis進行唯一鍵判定了。
do函數(shù)被unique裝飾,那么對于task_id對應(yīng)的任務(wù),全局只會執(zhí)行一次。
@unique def do(task_id): # do whatever pass
unique是怎樣實現(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)點:
1、使得功能邏輯和業(yè)務(wù)邏輯解耦,功能和業(yè)務(wù)的修改完全獨立,代碼結(jié)構(gòu)清晰,開發(fā)方便
2、一鍵注入,代碼復(fù)用程度高,擴展方便
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python 中pyqt5 樹節(jié)點點擊實現(xiàn)多窗口切換問題
這篇文章主要介紹了python 中pyqt5 樹節(jié)點點擊實現(xiàn)多窗口切換問題,文中給大家介紹了python pyqt5 點擊按鈕來打開另一個窗口的方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒借鑒價值,需要的朋友可以參考下2019-07-07淺談django model postgres的json字段編碼問題
下面小編就為大家分享一篇淺談django model postgres的json字段編碼問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01pandas apply 函數(shù) 實現(xiàn)多進程的示例講解
下面小編就為大家分享一篇pandas apply 函數(shù) 實現(xiàn)多進程的示例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04淺談pytorch中torch.max和F.softmax函數(shù)的維度解釋
這篇文章主要介紹了淺談pytorch中torch.max和F.softmax函數(shù)的維度解釋,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06