摘要:今天就結(jié)合最近的世界杯帶大家理解下裝飾器。而德國(guó)是上屆的冠軍,又是這屆奪冠熱門(mén)。裝飾器的存在是為了適用兩個(gè)場(chǎng)景,一個(gè)是增強(qiáng)被裝飾函數(shù)的行為,另一個(gè)是代碼重用。在利用語(yǔ)法糖,簡(jiǎn)化賦值操作。行為良好的裝飾器可以重用,以減少代碼量。
Python 裝飾器是在面試過(guò)程高頻被問(wèn)到的問(wèn)題,裝飾器也是一個(gè)非常好用的特性,
熟練掌握裝飾器會(huì)讓你的編程思路更加寬廣,程序也更加 pythonic。
今天就結(jié)合最近的世界杯帶大家理解下裝飾器。
德國(guó)戰(zhàn)車(chē)6 月 17 日德國(guó)戰(zhàn)墨西哥,小癡雖然是一個(gè)偽球迷,但每年的世界杯還是會(huì)了解下。而德國(guó)是上屆的冠軍,又是這屆奪冠熱門(mén)。德意志戰(zhàn)車(chē)在 32 年間小組賽就沒(méi)有輸過(guò)!臥槽!雖然小癡很少賭球,但這次德國(guó)如此強(qiáng)大,肯定會(huì)贏吧。搏一搏單車(chē)變摩托!隨后小癡買(mǎi)了德國(guó)隊(duì)贏。心里想著這次肯定穩(wěn)了!贏了會(huì)所嫩模!小癡連比賽都不看,美滋滋的敲著代碼。
然后比賽結(jié)果卻是德國(guó)爆冷 0:1 輸給墨西哥隊(duì),德國(guó)隊(duì)輸了比賽,小癡也下海干活。只是此時(shí)的天臺(tái)有點(diǎn)擠,風(fēng)還有大。
小癡含淚的寫(xiě)下了下面的代碼:
def guess_win(func): def rooftop_status(): result = func() print("天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)!") return result return rooftop_status @guess_win def german_team(): print("德國(guó)必勝!")
輸出結(jié)果:
德國(guó)必勝! 天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)!裝飾器是什么
首先我們先來(lái)了解下什么是裝飾器,嚴(yán)格來(lái)說(shuō),裝飾器只是語(yǔ)法糖,裝飾器是可調(diào)用的對(duì)象,可以像常規(guī)的可調(diào)用對(duì)象那樣調(diào)用,特殊的地方是裝飾器的參數(shù)是一個(gè)函數(shù)。
裝飾器的存在是為了適用兩個(gè)場(chǎng)景,一個(gè)是增強(qiáng)被裝飾函數(shù)的行為,另一個(gè)是代碼重用。
比如在上面的例子中我們?cè)趬旱聡?guó)隊(duì)贏的時(shí)候,原本的 german_team() 函數(shù)只是輸出德國(guó)必勝,但在使用裝飾器(guess_win)后,它的功能多了一項(xiàng):輸出「天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)!」。這就是一個(gè)簡(jiǎn)單的裝飾器,實(shí)現(xiàn)了「增強(qiáng)被裝飾函數(shù)的行為」。
一個(gè)良好的裝飾器必須要遵守兩個(gè)原則:
1 不能修改被裝飾函數(shù)的代碼
2 不能修改被裝飾函數(shù)的調(diào)用方式
這里并不難以理解,在現(xiàn)在的生產(chǎn)環(huán)境中,很多代碼是不能輕易的改寫(xiě),因?yàn)檫@樣有可能發(fā)送意想不到的影響。還有一點(diǎn)就是我們?cè)诳创笊竦拇a,我們根本不懂如何改寫(xiě)。同時(shí)你也不能修改調(diào)用方式,因?yàn)槟悴⒉恢烙性谝粋€(gè)項(xiàng)目中,有多少處應(yīng)用了此函數(shù)。
裝飾器理解基礎(chǔ)如果你想要很好的理解裝飾器,那下面的兩個(gè)內(nèi)容需要你先有所認(rèn)知。
1 函數(shù)名可以賦值給變量
2 高階函數(shù)
1 函數(shù)名可以賦值給變量我們來(lái)看下這個(gè)例子:
def func(name): print("我是{}!慌的一逼!".format(name)) func("梅西") y = func y("勒夫")
輸出結(jié)果:
我是梅西!慌的一逼! 我是勒夫!慌的一逼!
在代碼中我們首先定義了函數(shù) func,并調(diào)用了 func 函數(shù),并且把 func 賦值給 y。y = func 表明了:函數(shù)名可以賦值給變量,并且不影響調(diào)用。
這樣講,可能還有些人不太明白。我們?cè)趤?lái)對(duì)比下我們常用的操作。這其實(shí)和整數(shù)、數(shù)字是一樣的,下面的代碼你肯定熟悉:
a = 1 b = a print(a, b)
2 高階函數(shù)
高階函數(shù)滿(mǎn)足如下的兩個(gè)條件中的任意一個(gè):a.可以接收函數(shù)名作為實(shí)參;b.返回值中可以包含函數(shù)名。
在 Python 標(biāo)準(zhǔn)庫(kù)中的 map 和 filter 等函數(shù)就是高階函數(shù)。
l = [1, 2, 4] r = map(lambda x: x*3, l) for i in r: print("當(dāng)前天臺(tái)人數(shù):", i)
輸出結(jié)果:
當(dāng)前天臺(tái)人數(shù): 3 當(dāng)前天臺(tái)人數(shù): 6 當(dāng)前天臺(tái)人數(shù): 12
自定義一個(gè)能返回函數(shù)的函數(shù),也是高階函數(shù):
def f(l): return map(lambda x: x *5, l) a = f(l) for i in a: print("當(dāng)前天臺(tái)人數(shù):", i)
輸出結(jié)果:
當(dāng)前天臺(tái)人數(shù): 5 當(dāng)前天臺(tái)人數(shù): 10 當(dāng)前天臺(tái)人數(shù): 20實(shí)現(xiàn)一個(gè)類(lèi)似的裝飾器
現(xiàn)在你已經(jīng)知道了「函數(shù)名賦值」和「高階函數(shù)」,有了這兩個(gè)基礎(chǔ),我們就可以嘗試實(shí)現(xiàn)一個(gè)類(lèi)似的裝飾器。
def status(func): print("慌的一逼!") return func def name(): print("我是梅西!") temp = status(name) temp()
輸出結(jié)果:
慌的一逼! 我是梅西!
在這個(gè)例子中我們定義了一個(gè) status 函數(shù),status 接收一個(gè)函數(shù)名然后直接返回該函數(shù)名。這樣我們實(shí)現(xiàn)了不修改原函數(shù) name,并且添加了一個(gè)新功能的需求。但是這里有個(gè)缺陷就是函數(shù)的調(diào)用方式改變了。即不是原本的 name,而是 temp。
要解決這個(gè)問(wèn)題很簡(jiǎn)單,相信 a = a*3 這樣的表達(dá)式大家都見(jiàn)過(guò),那么上述代碼中的 temp = status(name) 同樣可以修改為 name = status(name),這樣我們就完美的解決了問(wèn)題:既添加新功能又沒(méi)有修改原函數(shù)和其調(diào)用方式。修改后的代碼如下:
def status(func): print("慌的一逼!") return func def name(): print("我是梅西!") name = status(name) name()
但這樣的代碼卻有個(gè)不便之處,即每次使用這樣的裝飾器,我們都要寫(xiě)類(lèi)似 name = status(name) 的代碼。程序員都是懶的,所以才有那么多高級(jí)的語(yǔ)法。在 python 中為了簡(jiǎn)化這種情況,提供了一個(gè)語(yǔ)法糖 @,在每個(gè)被裝飾的函數(shù)上方使用這個(gè)語(yǔ)法糖就可以省掉這一句代碼 name = status(name),最后的代碼如下:
def status(func): print("慌的一逼!") return func @status def name(): print("我是梅西!") name()
這樣我們就弄清楚了裝飾器的工作原理:
1 寫(xiě)一個(gè)高階函數(shù),即參數(shù)是函數(shù),返回的也是函數(shù)。
2 在利用語(yǔ)法糖@,簡(jiǎn)化賦值操作。
但是對(duì)比開(kāi)頭的例子,還是有些不一樣。在開(kāi)始的例子中,我們還實(shí)現(xiàn)了一個(gè) rooftop_status 函數(shù),來(lái)判斷下當(dāng)前的天臺(tái)狀是否人滿(mǎn)。但是我們現(xiàn)在是直接返回了函數(shù)名,這樣函數(shù)調(diào)用后我們就沒(méi)辦法做任何事情。梅西和德國(guó)慌了,我們也慌了,各個(gè)都要天臺(tái)見(jiàn),但在這之前我們也要考慮下天臺(tái)的情況。
為了能判斷天臺(tái)的情況,所以此時(shí)我們需要在嵌套一層函數(shù),將實(shí)現(xiàn)額外功能的部分寫(xiě)在內(nèi)層函數(shù)中,然后將這個(gè)內(nèi)層函數(shù)返回即可。這也是為什么裝飾器都是嵌套函數(shù)的原因。
另外,開(kāi)篇的例子并沒(méi)有返回值,也沒(méi)有參數(shù),要對(duì)既有參數(shù)又有返回值的函數(shù)進(jìn)行裝飾的話(huà),還需要進(jìn)一步完善。 能夠處理返回值的裝飾器:
def guess_win(func): def rooftop_status(): result = func() print("天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)!") return result return rooftop_status @guess_win def german_team(): print("德國(guó)必勝!") return "贏了會(huì)所嫩模!輸了下海干活!" x = german_team() print(x)
輸出結(jié)果:
德國(guó)必勝! 天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)! 贏了會(huì)所嫩模!輸了下海干活!
能夠處理參數(shù)的裝飾器:
def guess_win(func): def rooftop_status(*args, **kwargs): result = func(*args, **kwargs) print("天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)!") return result return rooftop_status @guess_win def german_team(arg): print("{}必勝!".format(arg)) return "贏了會(huì)所嫩模!輸了下海干活!" x = german_team("德國(guó)") y = german_team("西班牙") print(x)
輸出結(jié)果:
德國(guó)必勝! 天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)! 西班牙必勝! 天臺(tái)已滿(mǎn),請(qǐng)排隊(duì)! 贏了會(huì)所嫩模!輸了下海干活!總結(jié)
裝飾器的本質(zhì)是函數(shù),其參數(shù)是另一個(gè)函數(shù)(被裝飾的函數(shù))。裝飾器通常會(huì)額外處理被裝飾的函數(shù),然后把它返回,或者將其替換成另一個(gè)函數(shù)或可調(diào)用對(duì)象。行為良好的裝飾器可以重用,以減少代碼量。
對(duì)于這屆的世界杯,我總結(jié)了下。
本文首發(fā)與公眾號(hào)「癡海」,后臺(tái)回復(fù)「1024」,領(lǐng)取 2018 最新 python 教程。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/42000.html
Python裝飾器為什么難理解? 無(wú)論項(xiàng)目中還是面試都離不開(kāi)裝飾器話(huà)題,裝飾器的強(qiáng)大在于它能夠在不修改原有業(yè)務(wù)邏輯的情況下對(duì)代碼進(jìn)行擴(kuò)展,權(quán)限校驗(yàn)、用戶(hù)認(rèn)證、日志記錄、性能測(cè)試、事務(wù)處理、緩存等都是裝飾器的絕佳應(yīng)用場(chǎng)景,它能夠最大程度地對(duì)代碼進(jìn)行復(fù)用。 但為什么初學(xué)者對(duì)裝飾器的理解如此困難,我認(rèn)為本質(zhì)上是對(duì)Py… Python 實(shí)現(xiàn)車(chē)牌定位及分割 作者用 Python 實(shí)現(xiàn)車(chē)牌定位及分割的實(shí)踐。 ...
摘要:時(shí)代,如果需要手動(dòng)繼承,如多態(tài)多態(tài)是指,不同的子類(lèi)對(duì)象調(diào)用相同的父類(lèi)方法,會(huì)產(chǎn)生多態(tài)多樣結(jié)果的編程特性。 參考:黑馬程序員教程 - Python基礎(chǔ) 面向?qū)ο?OOP三大特性,且三個(gè)特性是有順序的: 封裝 繼承 多態(tài) 封裝 指的就是把現(xiàn)實(shí)世界的事務(wù),封裝、抽象成編程里的對(duì)象,包括各種屬性和方法。這個(gè)一般都很簡(jiǎn)單,不需要多講。 唯一要注意的就是:推薦從小往大開(kāi)始封裝、開(kāi)發(fā)類(lèi)。比如手槍...
摘要:列表越界的列表類(lèi)似于動(dòng)態(tài)數(shù)組,沒(méi)有長(zhǎng)度的限制。比如對(duì)將要傳進(jìn)內(nèi)層函數(shù)的參數(shù)進(jìn)行檢測(cè)等,從而實(shí)現(xiàn)對(duì)參數(shù)的類(lèi)型進(jìn)行限制。對(duì)二維列表的每一維列表進(jìn)行長(zhǎng)度限制,不足指定長(zhǎng)度,自動(dòng)補(bǔ)充指定元素。 前言 作為一名python的腦殘粉,請(qǐng)先跟我念一遍python大法好。 其作為動(dòng)態(tài)語(yǔ)言的靈活,簡(jiǎn)介的代碼,確實(shí)在某些情況下確實(shí)比其他編程語(yǔ)言要好。但你有沒(méi)有想過(guò),有時(shí)這些靈活的語(yǔ)法,可能會(huì)造成一些糟糕的...
摘要:我們以測(cè)量函數(shù)運(yùn)行時(shí)間為例來(lái)講一講裝飾器的運(yùn)行原理。三更加通用的裝飾器前面兩部分講了裝飾器的原理,這一部分就講講要寫(xiě)出一個(gè)通用的裝飾器需要注意的問(wèn)題。首先就是參數(shù)的問(wèn)題,裝飾器返回的函數(shù)不是原來(lái)的函數(shù),函數(shù)的簽名也就和原來(lái)的函數(shù)簽名不一樣。 一、最簡(jiǎn)單的裝飾器 裝飾器是python中很基礎(chǔ)也很實(shí)用的一個(gè)特性。通過(guò)裝飾器我們可以很方便地為一些函數(shù)添加相同的功能。我們以測(cè)量函數(shù)運(yùn)行時(shí)間為例...
摘要:函數(shù)裝飾器和閉包嚴(yán)格來(lái)說(shuō),裝飾器只是語(yǔ)法糖。何時(shí)執(zhí)行裝飾器它們?cè)诒谎b飾的函數(shù)定義之后立即運(yùn)行。裝飾器突出了被裝飾的函數(shù)的作用,還便于臨時(shí)禁用某個(gè)促銷(xiāo)策略只需把裝飾器注釋掉。 函數(shù)裝飾器和閉包 嚴(yán)格來(lái)說(shuō),裝飾器只是語(yǔ)法糖。如前所示,裝飾器可以像常規(guī)的可調(diào)用對(duì)象那樣調(diào)用,其參數(shù)是另一個(gè)函數(shù)。有時(shí),這樣做更方便,尤其是做元編程(在運(yùn)行時(shí)改變程序的行為)時(shí)。 Python何時(shí)執(zhí)行裝飾器 它們?cè)?..
閱讀 2632·2021-10-14 09:47
閱讀 4909·2021-09-22 15:52
閱讀 3355·2019-08-30 15:53
閱讀 1428·2019-08-30 15:44
閱讀 669·2019-08-29 16:41
閱讀 1646·2019-08-29 16:28
閱讀 439·2019-08-29 15:23
閱讀 1618·2019-08-26 12:20