標籤:
一.decorator基礎。
最初接觸python是在大學畢業進入某一遊戲公司後,最開始覺得python不嚴謹,很不喜歡python這種“簡單”的指令碼,但是後來,越來越離不開python了,覺得這麼靈活方便的語言,簡直是程式員的福音,尤其是它的資料結構,他帶來了一個“{}” 大括弧就能搞定一切的時代。當然python還有很多優秀的特性。現在我們來講python的特性之一--decorator(裝飾器)。
裝飾器,顧名思義就是修飾函數或者類的,它把要裝飾的函數作為參數,然後在函數體內可以執行很多靈活的操作。這些修飾僅是當聲明一個函數或者方法的時候,才會應用的額外調用。裝飾器的文法以@開頭,接著是裝飾器函數的名字和可選的參數。跟著裝飾器聲明的是被修飾的函數,和裝飾函數的選擇性參數。裝飾器看起來會是這樣:
@decorator()def func(*args):passfunc(*args)
其解譯器會解釋成下面這樣的語句:
func = decorator(func)func(*args)
其實,寫過python的人都接觸過@decorator。比如寫一個單例類:
class Singleton(object):_instance = Nonedef __init__(self):print "init single"@classmethoddef instance(cls):if cls._instance is None:cls._instance = Singleton()return cls._instancedef func(self):print "call func"
@classmethod就是一個decorator,是python內建的裝飾器(還有@staticmethod...)
class Singleton(object):_instance = Nonedef __init__(self):print "init single"#@classmethoddef instance(cls):if cls._instance is None:cls._instance = Singleton()return cls._instanceinstance = classmethod(instance)def func(self):print "call func"Singleton.instance().func()
以上跟這個效果是一樣的。只不過卻讓instance少寫了兩次。
二.decorator的用法。
1.修飾函數:
decorator修飾函數有幾種用法。
1).單個Decorator,不帶參數
def deco(func):def call_method(*args):print "before call method"func(*args)print "after call method"return call_method@deco()def func(a,b):print "in func"return a + bfunc(1,2)
輸出為:
before call method
in func
after call method
以上對於func的調用 跟以下是一樣的(注釋掉@deco()):
func = deco(func)func(1,2)
再寫的明白點:
call_method = deco(func) #deco這個函數返回call_method
call_method(*args) # 是不是恍然大悟了。
當然,最初的寫法是最簡單的,它讓你每一個調用func()的地方都自動調用了@deco(),然後執行一些額外的操作。比如 print "after call method".當然你可以做你想做的任何事,比如在這裡記log,你就不需要邊CTRL+C,CTRL+V在每一個調用func的地方加上相同的代碼了。
2).單個decorator,帶參數。
def deco(a):def real_deco(func):def call_method(*args):print "before call method"result = a + func(*args)print "after call method ",resultreturn call_methodreturn real_deco@deco("Hello ")def func(name):print "in func ",namereturn namefunc("World!")func("No World!")
輸出為:
before call method
in func World!
after call method Hello World!
before call method
in func No World!
after call method Hello No World!
總結:
無參數decorator:把函數名傳進去,然後產生一個新的裝飾器函數
有參decorator:有參裝飾,裝飾函數先處理參數,再產生一個新的裝飾器函數,然後對函數進行裝飾
3).decorator還可以有多個,如所示:
@dec_a@dec_b@dec_cdef method(args): passprint "=========="method = dec_a(dec_b(dec_c(method)))
只不過,平時項目中極少用到,我也就不講了。
那麼什麼是裝飾器?
現在我們知道裝飾器實際就是函數。我們也知道他們接受函數對象。但它們是怎樣處理那些函數的呢?一般說來,當你封裝一個函數的時候,你最終會調用它。最棒的是我們能在封裝的環境下在合適的時機調用它。我們在執行函數之前,可以運行些預備代碼,也可以在執行代碼之後做些清理工作。所以當你看見一個裝飾器函數的時候,很可能在裡面找到這樣一些代碼,它定義了某個函數並在定義內的某處嵌入了對目標函數的調用或者至少一些引用。從本質上看,這些特徵引入了java 開發人員稱呼之為AOP(Aspect Oriented Programming,面向方面編程)的概念。
你可以考慮在裝飾器中置入通用功能的代碼來降低程式複雜度。例如,可以用裝飾器來:
引入日誌
增加計時邏輯來檢測效能
給函數加入事務的能力
2.修飾類:
修飾類和修飾函數差不多,修飾類是把@decorator放在class上面,用以修改類的屬性
<span style="font-size:14px;">def func(self):return "func"def deco(kclass):kclass.func = funckclass.name = "Hello"return kclass@decoclass test(object):def __init__(self):print "in init",self.nameprint "call func: ",self.func()test()</span>
輸出:
in init Hello
call func: func
三.項目實踐
1.最近項目中用到decorator比較多。其中有修飾類的:
<span style="font-size:14px;">def is_persistent(_self):return Truedef Persistent(klass):"""類的decorator,用來修飾Entity的子類。如:@Persistentclass player(Entity):...這樣的類才會被序列化到mongodb中"""klass.is_persistent = is_persistentreturn klass@Persistentclass player(Entity):pass</span>
相當於在 player裡面定義了一個 is_persistent()的方法。在需要Persistent的地方加上@Persistent就行了,當然也可以在每個需要@Persistent的類裡面重寫這個方法,但是當對象多的時候,自己也不知道自己寫了沒有了。
2.用在callback裡面,但是callback的參數有的存在,有的還需要經過複雜的操作擷取,假如你要經過一個非同步操作,操作回來調用func(requestId,*args),requestId是已知的,但是*args是回調回來才有的,你肯定有地方可以存requestId,然後回調回來,再取值進行func操作,但是使用類似decorator的操作可以簡化這一操作:
<span style="font-size:14px;">def request_decorator(requestId, func):"""傳遞請求序號參數,用於回調:param func::return:"""def requestId_callback(*args):return func(requestId, *args)return requestId_callbackcallback = request_decorator(requestId, self.call_func)...args = ...callback(*args)</span>
巧妙的把已有的參數傳進去,然後等其他參數回來就可以了。
python深入學習--decorator強大的裝飾器