就是一個(gè)callable object。 它使python編程更加容易。
創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為北辰企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì),北辰網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
例如:
@dec
def A(args):
pass
它就等價(jià)于dec(A). 當(dāng)然還有帶參數(shù)的decorator。我就不舉例了。
python文檔里有這樣一句話。
A function definition may be wrapped by one or more decorator expressions. Decorator expressions are evaluated when the function is defined, in the scope that contains the function definition. The result must be a callable, which is invoked with the function object as the only argument. The returned value is bound to the function name instead of the function object. Multiple decorators are applied in nested fashion.
大概就是說函數(shù)的定義可以用多個(gè)decorator。decorator就在函數(shù)定義時(shí)用函數(shù)作為參數(shù)調(diào)用,然后返回一個(gè)可調(diào)用對象。 所以寫decorator的時(shí)候一定要返回一個(gè)可調(diào)用對象。
不知道你明白沒。
【@】符號在python中是裝飾器的意思。
裝飾器對一個(gè)可調(diào)用對象(函數(shù)、方法、類等等)進(jìn)行裝飾,它返回的也是一個(gè)可調(diào)用對象。
一般情況下,裝飾器是對被裝飾對象的修飾與增強(qiáng)。用現(xiàn)實(shí)事物類比的話,可以類比為中間商:中間商不生產(chǎn)產(chǎn)品,它將工廠生產(chǎn)的產(chǎn)品進(jìn)行包裝、運(yùn)輸后再銷售給顧客。裝飾器不實(shí)現(xiàn)核心功能,它提供對目標(biāo)函數(shù)調(diào)用的封裝與強(qiáng)。
它裝飾的方法返回值是一個(gè)對象(BillList、Bill、List[BillDetail]等),而裝飾器【enabled_cache】的作用如它的名稱一樣:使用緩存。可以看到,這個(gè)裝飾器函數(shù)中定義了一個(gè)函數(shù)【wrapper】然后將這個(gè)wrapper作為返回值。這樣,原本調(diào)用ProductionBos.bill_with_last_week的代碼就不需要做任何改變就能享受到ProductionBos.bill_with_last_week原有的功能(得到一個(gè)BillList對象)和enabled_cache提供的附加功能(如果該對象有緩存,就不再從數(shù)據(jù)庫查詢)。
先來看一個(gè)簡單例子:
def foo():
print('i am foo')
現(xiàn)在有一個(gè)新的需求,希望可以記錄下函數(shù)的執(zhí)行日志,于是在代碼中添加日志代碼:
def foo():
print('i am foo')
logging.info("foo is running")
bar()、bar2()也有類似的需求,怎么做?再寫一個(gè)logging在bar函數(shù)里?這樣就造成大量雷同的代碼,為了減少重復(fù)寫代碼,我們可以這樣做,重新定義一個(gè)函數(shù):專門處理日志 ,日志處理完之后再執(zhí)行真正的業(yè)務(wù)代碼
def use_logging(func):
logging.warn("%s is running" % func.__name__)
func()
def bar():
print('i am bar')
use_logging(bar)
邏輯上不難理解, 但是這樣的話,我們每次都要將一個(gè)函數(shù)作為參數(shù)傳遞給use_logging函數(shù)。而且這種方式已經(jīng)破壞了原有的代碼邏輯結(jié)構(gòu),之前執(zhí)行業(yè)務(wù)邏輯時(shí),執(zhí)行運(yùn)行bar(),但是現(xiàn)在不得不改成use_logging(bar)。那么有沒有更好的方式的呢?當(dāng)然有,答案就是裝飾器。
簡單裝飾器
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
def bar():
print('i am bar')
bar = use_logging(bar)
bar()
函數(shù)use_logging就是裝飾器,它把執(zhí)行真正業(yè)務(wù)方法的func包裹在函數(shù)里面,看起來像bar被use_logging裝飾了。在這個(gè)例子中,函數(shù)進(jìn)入和退出時(shí) ,被稱為一個(gè)橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。
@符號是裝飾器的語法糖,在定義函數(shù)的時(shí)候使用,避免再一次賦值操作
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
@use_logging
def foo():
print("i am foo")
@use_logging
def bar():
print("i am bar")
bar()
如上所示,這樣我們就可以省去bar = use_logging(bar)這一句了,直接調(diào)用bar()即可得到想要的結(jié)果。如果我們有其他的類似函數(shù),我們可以繼續(xù)調(diào)用裝飾器來修飾函數(shù),而不用重復(fù)修改函數(shù)或者增加新的封裝。這樣,我們就提高了程序的可重復(fù)利用性,并增加了程序的可讀性。
裝飾器在Python使用如此方便都要?dú)w因于Python的函數(shù)能像普通的對象一樣能作為參數(shù)傳遞給其他函數(shù),可以被賦值給其他變量,可以作為返回值,可以被定義在另外一個(gè)函數(shù)內(nèi)。
帶參數(shù)的裝飾器
裝飾器還有更大的靈活性,例如帶參數(shù)的裝飾器:在上面的裝飾器調(diào)用中,比如@use_logging,該裝飾器唯一的參數(shù)就是執(zhí)行業(yè)務(wù)的函數(shù)。裝飾器的語法允許我們在調(diào)用時(shí),提供其它參數(shù),比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。
def use_logging(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator
@use_logging(level="warn")
def foo(name='foo'):
print("i am %s" % name)
foo()
上面的use_logging是允許帶參數(shù)的裝飾器。它實(shí)際上是對原有裝飾器的一個(gè)函數(shù)封裝,并返回一個(gè)裝飾器。我們可以將它理解為一個(gè)含有參數(shù)的閉包。當(dāng)我 們使用@use_logging(level="warn")調(diào)用的時(shí)候,Python能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。
類裝飾器
再來看看類裝飾器,相比函數(shù)裝飾器,類裝飾器具有靈活度大、高內(nèi)聚、封裝性等優(yōu)點(diǎn)。使用類裝飾器還可以依靠類內(nèi)部的\_\_call\_\_方法,當(dāng)使用 @ 形式將裝飾器附加到函數(shù)上時(shí),就會(huì)調(diào)用此方法。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')
@Foo
def bar():
print ('bar')
bar()
functools.wraps
使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見了,比如函數(shù)的docstring、__name__、參數(shù)列表,先看例子:
裝飾器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
函數(shù)
@logged
def f(x):
"""does some math"""
return x + x * x
該函數(shù)完成等價(jià)于:
def f(x):
"""does some math"""
return x + x * x
f = logged(f)
不難發(fā)現(xiàn),函數(shù)f被with_logging取代了,當(dāng)然它的docstring,__name__就是變成了with_logging函數(shù)的信息了。
print f.__name__ # prints 'with_logging'
print f.__doc__ # prints None
這個(gè)問題就比較嚴(yán)重的,好在我們有functools.wraps,wraps本身也是一個(gè)裝飾器,它能把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,這使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息了。
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'
內(nèi)置裝飾器
@staticmathod、@classmethod、@property
裝飾器的順序
@a
@b
@c
def f ():
等效于
f = a(b(c(f)))
循環(huán)是一種常用的程序控制結(jié)構(gòu)。我們常說,機(jī)器相比人類的最大優(yōu)點(diǎn)之一,就是機(jī)器可以不眠不休的重復(fù)做某件事情,但人卻不行。而“循環(huán)”,則是實(shí)現(xiàn)讓機(jī)器不斷重復(fù)工作的關(guān)鍵概念。
在循環(huán)語法方面,Python 表現(xiàn)的即傳統(tǒng)又不傳統(tǒng)。它雖然拋棄了常見的 for(init;condition;incrment) 三段式結(jié)構(gòu),但還是選擇了 for 和 while 這兩個(gè)經(jīng)典的關(guān)鍵字來表達(dá)循環(huán)。絕大多數(shù)情況下,我們的循環(huán)需求都可以用 forin來滿足, while相比之下用的則更少些。
雖然循環(huán)的語法很簡單,但是要寫好它確并不容易。在這篇文章里,我們將探討什么是“地道”的循環(huán)代碼,以及如何編寫它們。
什么是“地道”的循環(huán)?
“地道”這個(gè)詞,通常被用來形容某人做某件事情時(shí),非常符合當(dāng)?shù)貍鹘y(tǒng),做的非常好。打個(gè)比方,你去參加一個(gè)朋友聚會(huì),同桌的有一位廣東人,對方一開口,句句都是標(biāo)準(zhǔn)京腔、完美兒化音。那你可以對她說:“您的北京話說的真地道”。
既然“地道”這個(gè)詞形容的經(jīng)常是口音、做菜的口味這類實(shí)實(shí)在在的東西,那“地道”的循環(huán)代碼又是什么意思呢?讓我拿一個(gè)經(jīng)典的例子來解釋一下。
如果你去問一位剛學(xué)習(xí) Python 一個(gè)月的人:“如何在遍歷一個(gè)列表的同時(shí)獲取當(dāng)前下標(biāo)?”。他可能會(huì)交出這樣的代碼:
?
上面的循環(huán)雖然沒錯(cuò),但它確一點(diǎn)都不“地道”。一個(gè)擁有三年 Python 開發(fā)經(jīng)驗(yàn)的人會(huì)說,代碼應(yīng)該這么寫:
enumerate() 是 Python 的一個(gè)內(nèi)置函數(shù),它接收一個(gè)“可迭代”對象作為參數(shù),然后返回一個(gè)不斷生成 (當(dāng)前下標(biāo),當(dāng)前元素) 的新可迭代對象。這個(gè)場景使用它最適合不過。
所以,在上面的例子里,我們會(huì)認(rèn)為第二段循環(huán)代碼比第一段更“地道”。因?yàn)樗酶庇^的代碼,更聰明的完成了工作。
enumerate() 所代表的編程思路
不過,判斷某段循環(huán)代碼是否地道,并不僅僅是以知道或不知道某個(gè)內(nèi)置方法作為標(biāo)準(zhǔn)。我們可以從上面的例子挖掘出更深層的東西。
如你所見,Python 的 for 循環(huán)只有 forin這一種結(jié)構(gòu),而結(jié)構(gòu)里的前半部分 - 賦值給 item- 沒有太多花樣可玩。所以后半部分的 可迭代對象 是我們唯一能夠大做文章的東西。而以 enumerate() 函數(shù)為代表的“修飾函數(shù)”,剛好提供了一種思路:通過修飾可迭代對象來優(yōu)化循環(huán)本身。
這就引出了我的第一個(gè)建議。
建議1:使用函數(shù)修飾被迭代對象來優(yōu)化循環(huán)
使用修飾函數(shù)處理可迭代對象,可以在各種方面影響循環(huán)代碼。而要找到合適的例子來演示這個(gè)方法,并不用去太遠(yuǎn),內(nèi)置模塊 itertools 就是一個(gè)絕佳的例子。
簡單來說,itertools 是一個(gè)包含很多面向可迭代對象的工具函數(shù)集。我在之前的系列文章《容器的門道》里提到過它。
如果要學(xué)習(xí) itertools,那么 Python 官方文檔 是你的首選,里面有非常詳細(xì)的模塊相關(guān)資料。但在這篇文章里,側(cè)重點(diǎn)將和官方文檔稍有不同。我會(huì)通過一些常見的代碼場景,來詳細(xì)解釋它是如何改善循環(huán)代碼的。
1. 使用 product 扁平化多層嵌套循環(huán)
雖然我們都知道“扁平的代碼比嵌套的好”。但有時(shí)針對某類需求,似乎一定得寫多層嵌套循環(huán)才行。比如下面這段:
?
對于這種需要嵌套遍歷多個(gè)對象的多層循環(huán)代碼,我們可以使用 product() 函數(shù)來優(yōu)化它。product() 可以接收多個(gè)可迭代對象,然后根據(jù)它們的笛卡爾積不斷生成結(jié)果。
?
相比之前的代碼,使用 product() 的函數(shù)只用了一層 for 循環(huán)就完成了任務(wù),代碼變得更精煉了。
2. 使用 islice 實(shí)現(xiàn)循環(huán)內(nèi)隔行處理
有一份包含 Reddit 帖子標(biāo)題的外部數(shù)據(jù)文件,里面的內(nèi)容格式是這樣的:
?
可能是為了美觀,在這份文件里的每兩個(gè)標(biāo)題之間,都有一個(gè) "---" 分隔符?,F(xiàn)在,我們需要獲取文件里所有的標(biāo)題列表,所以在遍歷文件內(nèi)容的過程中,必須跳過這些無意義的分隔符。
參考之前對 enumerate() 函數(shù)的了解,我們可以通過在循環(huán)內(nèi)加一段基于當(dāng)前循環(huán)序號的 if 判斷來做到這一點(diǎn):
?
但對于這類在循環(huán)內(nèi)進(jìn)行隔行處理的需求來說,如果使用 itertools 里的 islice() 函數(shù)修飾被循環(huán)對象,可以讓循環(huán)體代碼變得更簡單直接。
islice(seq,start,end,step) 函數(shù)和數(shù)組切片操作( list[start:stop:step] )有著幾乎一模一樣的參數(shù)。如果需要在循環(huán)內(nèi)部進(jìn)行隔行處理的話,只要設(shè)置第三個(gè)遞進(jìn)步長參數(shù) step 值為 2 即可(默認(rèn)為 1)。
?
3. 使用 takewhile 替代 break 語句
有時(shí),我們需要在每次循環(huán)開始時(shí),判斷循環(huán)是否需要提前結(jié)束。比如下面這樣:
?
對于這類需要提前中斷的循環(huán),我們可以使用 takewhile() 函數(shù)來簡化它。takewhile(predicate,iterable)會(huì)在迭代 iterable 的過程中不斷使用當(dāng)前對象作為參數(shù)調(diào)用 predicate 函數(shù)并測試返回結(jié)果,如果函數(shù)返回值為真,則生成當(dāng)前對象,循環(huán)繼續(xù)。否則立即中斷當(dāng)前循環(huán)。
使用 takewhile 的代碼樣例:
?
itertools 里面還有一些其他有意思的工具函數(shù),他們都可以用來和循環(huán)搭配使用,比如使用 chain 函數(shù)扁平化雙層嵌套循環(huán)、使用 zip_longest 函數(shù)一次同時(shí)循環(huán)多個(gè)對象等等。
篇幅有限,我在這里不再一一介紹。如果有興趣,可以自行去官方文檔詳細(xì)了解。
4. 使用生成器編寫自己的修飾函數(shù)
除了 itertools 提供的那些函數(shù)外,我們還可以非常方便的使用生成器來定義自己的循環(huán)修飾函數(shù)。
讓我們拿一個(gè)簡單的函數(shù)舉例:
?
在上面的函數(shù)里,循環(huán)體內(nèi)為了過濾掉所有奇數(shù),引入了一條額外的 if 判斷語句。如果要簡化循環(huán)體內(nèi)容,我們可以定義一個(gè)生成器函數(shù)來專門進(jìn)行偶數(shù)過濾:
?
將 numbers 變量使用 even_only 函數(shù)裝飾后, sum_even_only_v2 函數(shù)內(nèi)部便不用繼續(xù)關(guān)注“偶數(shù)過濾”邏輯了,只需要簡單完成求和即可。
Hint:當(dāng)然,上面的這個(gè)函數(shù)其實(shí)并不實(shí)用。在現(xiàn)實(shí)世界里,這種簡單需求最適合直接用生成器/列表表達(dá)式搞定:sum(numfornuminnumbersifnum%2==0)
建議2:按職責(zé)拆解循環(huán)體內(nèi)復(fù)雜代碼塊
我一直覺得循環(huán)是一個(gè)比較神奇的東西,每當(dāng)你寫下一個(gè)新的循環(huán)代碼塊,就好像開辟了一片黑魔法陣,陣內(nèi)的所有內(nèi)容都會(huì)開始無休止的重復(fù)執(zhí)行。
但我同時(shí)發(fā)現(xiàn),這片黑魔法陣除了能帶來好處,它還會(huì)引誘你不斷往陣內(nèi)塞入越來越多的代碼,包括過濾掉無效元素、預(yù)處理數(shù)據(jù)、打印日志等等。甚至一些原本不屬于同一抽象的內(nèi)容,也會(huì)被塞入到同一片黑魔法陣內(nèi)。
修飾符,比如說
class A:
@staticmethod
def m(self):
pass
就相當(dāng)于
class A:
def m(self):
pass
m = staticmethod(m)
其實(shí)就是一調(diào)用一個(gè)函數(shù)參數(shù)為下行的變量,并且替換它
擴(kuò)展資料:
函數(shù)修飾符
@用做函數(shù)的修飾符,可以在模塊或者類的定義層內(nèi)對函數(shù)進(jìn)行修飾,出現(xiàn)在函數(shù)定義的前一行,不允許和函數(shù)定義在同一行。
一個(gè)修飾符就是一個(gè)函數(shù),它將被修飾的函數(shù)作為參數(shù),并返回修飾后的同名函數(shù)或其他可調(diào)用的東西。
在Python的函數(shù)中偶爾會(huì)看到函數(shù)定義的上一行有@functionName的修飾,當(dāng)解釋器讀到@這樣的修飾符的時(shí)候會(huì)優(yōu)先解除@后的內(nèi)容,直接就把@的下一行的函數(shù)或者類作為@后邊函數(shù)的參數(shù),然后將返回值賦給下一個(gè)修飾的函數(shù)對象。
參考資料來源:百度百科-Python (計(jì)算機(jī)程序設(shè)計(jì)語言)
分享文章:包含python修飾函數(shù)嗎的詞條
URL網(wǎng)址:http://aaarwkj.com/article4/dochooe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、自適應(yīng)網(wǎng)站、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站內(nèi)鏈、企業(yè)建站、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)