日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

Python-理解装饰器

發(fā)布時(shí)間:2023/12/9 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python-理解装饰器 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章先由stackoverflow上面的一個(gè)問(wèn)題引起吧,如果使用如下的代碼:

@makebold @makeitalic def say():return "Hello"

打印出如下的輸出:

<b><i>Hello<i></b>

你會(huì)怎么做?最后給出的答案是:

def makebold(fn):def wrapped():return "<b>" + fn() + "</b>"return wrappeddef makeitalic(fn):def wrapped():return "<i>" + fn() + "</i>"return wrapped@makebold @makeitalic def hello():return "hello world"print hello() ## 返回 <b><i>hello world</i></b>

現(xiàn)在我們來(lái)看看如何從一些最基礎(chǔ)的方式來(lái)理解Python的裝飾器。英文討論參考? Here 。
1.1. 需求是怎么來(lái)的?

裝飾器是一個(gè)很著名的設(shè)計(jì)模式,經(jīng)常被用于有切面需求的場(chǎng)景,較為經(jīng)典的有插入日志、性能測(cè)試、事務(wù)處理等。裝飾器是解決這類(lèi)問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量函數(shù)中與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。

裝飾器的定義很是抽象,我們來(lái)看一個(gè)小例子。

def foo():print 'in foo()' foo()

這是一個(gè)很無(wú)聊的函數(shù)沒(méi)錯(cuò)。但是突然有一個(gè)更無(wú)聊的人,我們稱(chēng)呼他為B君,說(shuō)我想看看執(zhí)行這個(gè)函數(shù)用了多長(zhǎng)時(shí)間,好吧,那么我們可以這樣做:

import time def foo():start = time.clock()print 'in foo()'end = time.clock()print 'used:', end - startfoo()

很好,功能看起來(lái)無(wú)懈可擊??墒堑疤鄣腂君此刻突然不想看這個(gè)函數(shù)了,他對(duì)另一個(gè)叫foo2的函數(shù)產(chǎn)生了更濃厚的興趣。

怎么辦呢?如果把以上新增加的代碼復(fù)制到foo2里,這就犯了大忌了~復(fù)制什么的難道不是最討厭了么!而且,如果B君繼續(xù)看了其他的函數(shù)呢?
1.2. 以不變應(yīng)萬(wàn)變,是變也 ?

還記得嗎,函數(shù)在Python中是一等公民,那么我們可以考慮重新定義一個(gè)函數(shù)timeit,將foo的引用傳遞給他,然后在timeit中調(diào)用foo并進(jìn)行計(jì)時(shí),這樣,我們就達(dá)到了不改動(dòng)foo定義的目的,而且,不論B君看了多少個(gè)函數(shù),我們都不用去修改函數(shù)定義了!

import timedef foo():print 'in foo()'def timeit(func):start = time.clock()func()end =time.clock()print 'used:', end - starttimeit(foo)

看起來(lái)邏輯上并沒(méi)有問(wèn)題,一切都很美好并且運(yùn)作正常!……等等,我們似乎修改了調(diào)用部分的代碼。原本我們是這樣調(diào)用的:foo(),修改以后變成了:timeit(foo)。這樣的話(huà),如果foo在N處都被調(diào)用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調(diào)用的代碼無(wú)法修改這個(gè)情況,比如:這個(gè)函數(shù)是你交給別人使用的。
1.3. 最大限度地少改動(dòng)! ?

既然如此,我們就來(lái)想想辦法不修改調(diào)用的代碼;如果不修改調(diào)用代碼,也就意味著調(diào)用foo()需要產(chǎn)生調(diào)用timeit(foo)的效果。我們可以想到將timeit賦值給foo,但是timeit似乎帶有一個(gè)參數(shù)……想辦法把參數(shù)統(tǒng)一吧!如果timeit(foo)不是直接產(chǎn)生調(diào)用效果,而是返回一個(gè)與foo參數(shù)列表一致的函數(shù)的話(huà)……就很好辦了,將timeit(foo)的返回值賦值給foo,然后,調(diào)用foo()的代碼完全不用修改!

#-*- coding: UTF-8 -*- import timedef foo():print 'in foo()'# 定義一個(gè)計(jì)時(shí)器,傳入一個(gè),并返回另一個(gè)附加了計(jì)時(shí)功能的方法 def timeit(func):# 定義一個(gè)內(nèi)嵌的包裝函數(shù),給傳入的函數(shù)加上計(jì)時(shí)功能的包裝def wrapper():start = time.clock()func()end =time.clock()print 'used:', end - start# 將包裝后的函數(shù)返回return wrapperfoo = timeit(foo) foo()

這樣,一個(gè)簡(jiǎn)易的計(jì)時(shí)器就做好了!我們只需要在定義foo以后調(diào)用foo之前,加上foo = timeit(foo),就可以達(dá)到計(jì)時(shí)的目的,這也就是裝飾器的概念,看起來(lái)像是foo被timeit裝飾了。在在這個(gè)例子中,函數(shù)進(jìn)入和退出時(shí)需要計(jì)時(shí),這被稱(chēng)為一個(gè)橫切面(Aspect),這種編程方式被稱(chēng)為面向切面的編程(Aspect-Oriented Programming)。與傳統(tǒng)編程習(xí)慣的從上往下執(zhí)行方式相比較而言,像是在函數(shù)執(zhí)行的流程中橫向地插入了一段邏輯。在特定的業(yè)務(wù)領(lǐng)域里,能減少大量重復(fù)代碼。面向切面編程還有相當(dāng)多的術(shù)語(yǔ),這里就不多做介紹,感興趣的話(huà)可以去找找相關(guān)的資料。
這個(gè)例子僅用于演示,并沒(méi)有考慮foo帶有參數(shù)和有返回值的情況,完善它的重任就交給你了 :)

上面這段代碼看起來(lái)似乎已經(jīng)不能再精簡(jiǎn)了,Python于是提供了一個(gè)語(yǔ)法糖來(lái)降低字符輸入量。

import timedef timeit(func):def wrapper():start = time.clock()func()end =time.clock()print 'used:', end - startreturn wrapper@timeit def foo():print 'in foo()'foo()

重點(diǎn)關(guān)注第11行的@timeit,在定義上加上這一行與另外寫(xiě)foo = timeit(foo)完全等價(jià),千萬(wàn)不要以為@有另外的魔力。除了字符輸入少了一些,還有一個(gè)額外的好處:這樣看上去更有裝飾器的感覺(jué)。

1.4 最后回答前面提到的問(wèn)題:

# 裝飾器makebold用于轉(zhuǎn)換為粗體 def makebold(fn):# 結(jié)果返回該函數(shù)def wrapper():# 插入一些執(zhí)行前后的代碼return "<b>" + fn() + "</b>"return wrapper# 裝飾器makeitalic用于轉(zhuǎn)換為斜體 def makeitalic(fn):# 結(jié)果返回該函數(shù)def wrapper():# 插入一些執(zhí)行前后的代碼return "<i>" + fn() + "</i>"return wrapper # 注意順序 @makebold @makeitalic def say():return "hello"print say() #輸出: <b><i>hello</i></b># 等同于 def say():return "hello" say = makebold(makeitalic(say))print say() #輸出: <b><i>hello</i></b>

2、裝飾器的種類(lèi)

2.1 無(wú)參數(shù)裝飾器

def deco(func):print funcreturn func @deco def foo():pass foo()

第一個(gè)函數(shù)deco是裝飾函數(shù),它的參數(shù)就是被裝飾的函數(shù)對(duì)象。我們可以在deco函數(shù)內(nèi)對(duì)傳入的函數(shù)對(duì)象做一番“裝飾”,然后返回這個(gè)對(duì)象(記住一定要返回 ,不然外面調(diào)用foo的地方將會(huì)無(wú)函數(shù)可用。實(shí)際上此時(shí)foo=deco(foo)

我寫(xiě)了個(gè)小例子,檢查函數(shù)有沒(méi)有說(shuō)明文檔:

def deco_functionNeedDoc(func):if func.__doc__ == None :print func, "has no __doc__, it's a bad habit."else:print func, ':', func.__doc__, '.'return func @deco_functionNeedDoc def f():print 'f() Do something' @deco_functionNeedDoc def g():'I have a __doc__'print 'g() Do something' f() g()

2.2 有參數(shù)裝飾器

def decomaker(arg):'通常對(duì)arg會(huì)有一定的要求'"""由于有參數(shù)的decorator函數(shù)在調(diào)用時(shí)只會(huì)使用應(yīng)用時(shí)的參數(shù)而不接收被裝飾的函數(shù)做為參數(shù),所以必須在其內(nèi)部再創(chuàng)建一個(gè)函數(shù)"""def newDeco(func): #定義一個(gè)新的decorator函數(shù)print func, argreturn funcreturn newDeco @decomaker(deco_args) def foo():pass foo()

第一個(gè)函數(shù)decomaker是裝飾函數(shù),它的參數(shù)是用來(lái)加強(qiáng)“加強(qiáng)裝飾”的。由于此函數(shù)并非被裝飾的函數(shù)對(duì)象,所以在內(nèi)部必須至少創(chuàng)建一個(gè)接受被裝飾函數(shù)的函數(shù),然后返回這個(gè)對(duì)象(實(shí)際上此時(shí)foo=decomaker(arg)(foo))

這個(gè)我還真想不出什么好例子,還是見(jiàn)識(shí)少啊,只好借用同步鎖的例子了:

def synchronized(lock):"""鎖同步裝飾方法!lock必須實(shí)現(xiàn)了acquire和release方法"""def sync_with_lock(func):def new_func(*args, **kwargs):lock.acquire()try:return func(*args, **kwargs)finally:lock.release()new_func.func_name = func.func_namenew_func.__doc__ = func.__doc__return new_funcreturn sync_with_lock @synchronized(__locker) def update(data): """更新計(jì)劃任務(wù)"""tasks = self.get_tasks()delete_task = Nonefor task in tasks:if task[PLANTASK.ID] == data[PLANTASK.ID]:tasks.insert(tasks.index(task), data)tasks.remove(task)delete_task = taskr, msg = self._refresh(tasks, delete_task)return r, msg, data[PLANTASK.ID]

調(diào)用時(shí)還是updae(data)。

同時(shí)還可以將多個(gè)裝飾器組合 使用,注意調(diào)用順序:

@synchronized(__locker) @deco_functionNeedDoc def f():print 'f() Do something'

2.3 內(nèi)置的裝飾器

內(nèi)置的裝飾器有三個(gè),分別是staticmethod、classmethod和property,作用分別是把類(lèi)中定義的實(shí)例方法變成靜態(tài)方法、類(lèi)方法和類(lèi)屬性。由于模塊里可以定義函數(shù),所以靜態(tài)方法和類(lèi)方法的用處并不是太多,除非你想要完全的面向?qū)ο缶幊?。而屬性也不是不可或缺?#xff0c;Java沒(méi)有屬性也一樣活得很滋潤(rùn)。從我個(gè)人的Python經(jīng)驗(yàn)來(lái)看,我沒(méi)有使用過(guò)property,使用staticmethod和classmethod的頻率也非常低。

2.4 裝飾器進(jìn)階

http://www.cnblogs.com/JohnABC/p/4186209.html


具體請(qǐng)參考: http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html
轉(zhuǎn)自: http://www.open-open.com/lib/view/open1374584644558.html

轉(zhuǎn)載于:https://www.cnblogs.com/JohnABC/p/4091532.html

總結(jié)

以上是生活随笔為你收集整理的Python-理解装饰器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。