標籤:不能 stat rev 指令碼語言 好的 調用 this print icm
python 裝飾器
裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的傳回值也是一個函數對象。它經常用於有切面需求的情境,比如:插入日誌、效能測試、交易處理、緩衝、許可權校正等情境。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼並繼續重用。
寫一個簡單的裝飾器
def deco(f): def wrapped(*args, **kwargs): print "start" f(*args, **kwargs) print "end" return wrapped@decodef func(a): print afunc("func run")
運行代碼最終得到:
startfunc runend
通過範例程式碼可以更好的理解裝飾器的定義。
帶參數的裝飾器
def deco(level): def wrapped(f): def wrapper_inner(*args, **kwargs): print "%s start" % level f(*args, **kwargs) print "end" return wrapper_inner return wrapped@deco("i am info")def func(a): print afunc("func run")
如裝飾器定義中的使用,在列印日誌時,我們可能需要輸入不同的等級。
從這裡也可以看出裝飾器的參數傳入順序,從上到下,裝飾器參數,裝飾器修飾函數,和函數參數
內建裝飾器
內建裝飾器與普通裝飾器原理上不同,返回的是一個類對象,
@property
class TestEntiy(object): def __init__(self): super(TestEntiy, self).__init__() self._position = 5 @property def position(self): return self._position @position.setter def position(self, value): self._position = value @position.deleter def position(self): del self._position
使用@文法糖,更簡潔的實現get和set
但是要注意的是setter和deleter是property的第二三個參數,不能直接使用@文法,而應該使用已有的peoperty對象調用
@staticmethod和@classmethod
分別返回staticmethod和classmethod對象,來調用修飾函數,實現只能使用類調用而非對象調用
基於裝飾器實現event回調語義
在完成遊戲作業使用unity做用戶端,python做服務端。由於unity使用的指令碼語言是c#,因此在完成資料同步時,對於伺服器的資料進行分發時,使用了delegate & event語義來實現event回調,解決了資料管理實體與socket網路服務實體代碼之間的緊耦合。但是後來換了用戶端引擎之後使用python開發用戶端,為了實現event回調語義來解決服務端資料分發至各個資料管理實體,對於python語言的裝飾器文法糖進行了學習
def msg_listener(*events): def wrapper(func): func.events = events return func return wrapper
首先是裝飾器,對於被裝飾函數對象加上events屬性,這裡python很騷的地方,everything is object
然後我們再來看給每個函數加上的events是個什麼東西
class MsgNotifierEvent(object): _events = [] def __init__(self, name): super(MsgNotifierEvent, self).__init__() self._name = name self._callback = [] MsgNotifierEvent._events.append(self) def __iadd__(self, callback): self._callback.append(callback) return self def __call__(self, *args, **kwargs): for cb in self._callback: try: cb(*args, **kwargs) except Exception as e: print "msgNotifier callback error, function:", cb.__name__, e @classmethod def clear(cls, name): for event in cls._events: if event._name == name: event._callback = [] break
這裡從寫了__call__函數,因此主要的使用實在調用此對象時,那麼_callback裡面又是什麼呢?
以下是我用戶端資料管理的基類,主要是包含資料監聽的邏輯(Event回調),這裡可以看到init_data_listener函數擷取了所有包含events屬性的函數,將這些函數的都加到對應events的回調隊列中(這裡可以看上一段代碼對於__iadd__的重載),這樣就完整實現了通過監聽器MsgNotifierEvent的調用,實現了資料到來之後對於資料處理函數的回調
# -*- coding: utf-8 -*-import abcimport inspectclass ManagerBase(object): #包含資料接受的實體基類,需自己實現destroy函數,清除監聽器和事件 __metaclass__ = abc.ABCMeta def __init__(self): super(ManagerBase, self).__init__() self.init_data_listener() def init_data_listener(self): for listener_name, listener in inspect.getmembers(self, lambda f: hasattr(f, ‘events‘)): for event in listener.events: event += listener @abc.abstractmethod def destroy(self): raise NotImplementedError
以下為測試代碼
class TestEntiy(ManagerBase): def __init__(self): super(TestEntiy, self).__init__() @msg_listener(game_msg_recv) def update(self, protocol): print "this is deal data:", protocol def destroy(self): MsgNotifierEvent.clear("game_msg_recv") test = TestEntiy()game_msg_recv("i am test1")test.destroy()game_msg_recv("i am test2")#輸出#this is deal data: i am test1
python裝飾器學習