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