利用世界盃,讀懂 Python 裝飾器

Python 裝飾器是在面試過程高頻被問到的問題,裝飾器也是一個很是好用的特性,python

熟練掌握裝飾器會讓你的編程思路更加寬廣,程序也更加 pythonic。程序員

今天就結合最近的世界盃帶你們理解下裝飾器。面試

德國戰車編程

6 月 17 日德國戰墨西哥,小癡雖然是一個僞球迷,但每一年的世界盃仍是會了解下。而德國是上屆的冠軍,又是這屆奪冠熱門。德意志戰車在 32 年間小組賽就沒有輸過!臥槽!雖然小癡不多賭球,但此次德國如此強大,確定會贏吧。搏一搏單車變摩托!隨後小癡買了德國隊贏。內心想着此次確定穩了!贏了會所嫩模!小癡連比賽都不看,美滋滋的敲着代碼。微信

而後比賽結果倒是德國爆冷 0:1 輸給墨西哥隊,德國隊輸了比賽,小癡也下海乾活。只是此時的天台有點擠,風還有大。函數

小癡含淚的寫下了下面的代碼:學習

def guess_win(func):3d

def rooftop_status():orm

result = func()對象

print('天台已滿,請排隊!') return result return rooftop_status@guess_windef german_team():

print('德國必勝!')

複製代碼

輸出結果:

德國必勝!

天台已滿,請排隊!

複製代碼

裝飾器是什麼

首先咱們先來了解下什麼是裝飾器,嚴格來講,裝飾器只是語法糖,裝飾器是可調用的對象,能夠像常規的可調用對象那樣調用,特殊的地方是裝飾器的參數是一個函數。

裝飾器的存在是爲了適用兩個場景,一個是加強被裝飾函數的行爲,另外一個是代碼重用。

好比在上面的例子中咱們在壓德國隊贏的時候,本來的 german_team() 函數只是輸出德國必勝,但在使用裝飾器(guess_win)後,它的功能多了一項:輸出「天台已滿,請排隊!」。這就是一個簡單的裝飾器,實現了「加強被裝飾函數的行爲」。

一個良好的裝飾器必需要遵照兩個原則:

1 不能修改被裝飾函數的代碼

2 不能修改被裝飾函數的調用方式

這裏並不難以理解,在如今的生產環境中,不少代碼是不能輕易的改寫,由於這樣有可能發送意想不到的影響。還有一點就是咱們在看大神的代碼,咱們根本不懂如何改寫。同時你也不能修改調用方式,由於你並不知道有在一個項目中,有多少處應用了此函數。

裝飾器理解基礎

若是你想要很好的理解裝飾器,那下面的兩個內容須要你先有所認知。

1 函數名能夠賦值給變量

2 高階函數

1 函數名能夠賦值給變量

咱們來看下這個例子:

def func(name):

print('我是{}!慌的一逼!'.format(name))

func('梅西')

y = func

y('勒夫')

複製代碼

輸出結果:

我是梅西!慌的一逼!

我是勒夫!慌的一逼!

複製代碼

在代碼中咱們首先定義了函數 func,並調用了 func 函數,而且把 func 賦值給 y。y = func 代表了:函數名能夠賦值給變量,而且不影響調用。

這樣講,可能還有些人不太明白。咱們在來對比下咱們經常使用的操做。這其實和整數、數字是同樣的,下面的代碼你確定熟悉:

a = 1

b = a

print(a, b)

複製代碼

2 高階函數

高階函數知足以下的兩個條件中的任意一個:a.能夠接收函數名做爲實參;b.返回值中能夠包含函數名。

在 Python 標準庫中的 map 和 filter 等函數就是高階函數。

l = [1, 2, 4]

r = map(lambda x: x*3, l)for i in r:

print('當前天台人數:', i)

複製代碼

輸出結果:

當前天台人數: 3

當前天台人數: 6

當前天台人數: 12

複製代碼

自定義一個能返回函數的函數,也是高階函數:

def f(l):

return map(lambda x: x *5, l)

a = f(l)for i in a:

print('當前天台人數:', i)

複製代碼

輸出結果:

當前天台人數: 5

當前天台人數: 10

當前天台人數: 20

複製代碼

實現一個相似的裝飾器

如今你已經知道了「函數名賦值」和「高階函數」,有了這兩個基礎,咱們就能夠嘗試實現一個相似的裝飾器。

def status(func):

print('慌的一逼!') return funcdef name():

print('我是梅西!')

temp = status(name)

temp()

複製代碼

輸出結果:

慌的一逼!

我是梅西!

複製代碼

在這個例子中咱們定義了一個 status 函數,status 接收一個函數名而後直接返回該函數名。這樣咱們實現了不修改原函數 name,而且添加了一個新功能的需求。可是這裏有個缺陷就是函數的調用方式改變了。即不是本來的 name,而是 temp。

要解決這個問題很簡單,相信 a = a*3 這樣的表達式你們都見過,那麼上述代碼中的 temp = status(name) 一樣能夠修改成 name = status(name),這樣咱們就完美的解決了問題:既添加新功能又沒有修改原函數和其調用方式。修改後的代碼以下:

def status(func):

print('慌的一逼!') return funcdef name():

print('我是梅西!')

name = status(name)

name()

複製代碼

但這樣的代碼卻有個不便之處,即每次使用這樣的裝飾器,咱們都要寫相似 name = status(name) 的代碼。程序員都是懶的,因此纔有那麼多高級的語法。在 python 中爲了簡化這種狀況,提供了一個語法糖 @,在每一個被裝飾的函數上方使用這個語法糖就能夠省掉這一句代碼 name = status(name),最後的代碼以下:

def status(func):

print('慌的一逼!') return func@statusdef name():

print('我是梅西!')

name()

複製代碼

這樣咱們就弄清楚了裝飾器的工做原理:

1 寫一個高階函數,即參數是函數,返回的也是函數。

2 在利用語法糖@,簡化賦值操做。

可是對比開頭的例子,仍是有些不同。在開始的例子中,咱們還實現了一個 rooftop_status 函數,來判斷下當前的天台狀是否人滿。可是咱們如今是直接返回了函數名,這樣函數調用後咱們就沒辦法作任何事情。梅西和德國慌了,咱們也慌了,各個都要天台見,但在這以前咱們也要考慮下天台的狀況。

爲了能判斷天台的狀況,因此此時咱們須要在嵌套一層函數,將實現額外功能的部分寫在內層函數中,而後將這個內層函數返回便可。這也是爲何裝飾器都是嵌套函數的緣由。

另外,開篇的例子並無返回值,也沒有參數,要對既有參數又有返回值的函數進行裝飾的話,還須要進一步完善。 可以處理返回值的裝飾器:

def guess_win(func):

def rooftop_status():

result = func()

print('天台已滿,請排隊!') return result return rooftop_status@guess_windef german_team():

print('德國必勝!') return '贏了會所嫩模!輸了下海乾活!'x = german_team()

print(x)

複製代碼

輸出結果:

德國必勝!

天台已滿,請排隊!

贏了會所嫩模!輸了下海乾活!

複製代碼

可以處理參數的裝飾器:

def guess_win(func):

def rooftop_status(*args, **kwargs):

result = func(*args, **kwargs)

print('天台已滿,請排隊!') return result return rooftop_status@guess_windef german_team(arg):

print('{}必勝!'.format(arg)) return '贏了會所嫩模!輸了下海乾活!'x = german_team('德國')

y = german_team('西班牙')

print(x)

複製代碼

輸出結果:

德國必勝!

天台已滿,請排隊!

西班牙必勝!

天台已滿,請排隊!

贏了會所嫩模!輸了下海乾活!

複製代碼

總結

裝飾器的本質是函數,其參數是另外一個函數(被裝飾的函數)。裝飾器一般會額外處理被裝飾的函數,而後把它返回,或者將其替換成另外一個函數或可調用對象。行爲良好的裝飾器能夠重用,以減小代碼量。

對於這屆的世界盃,我總結了下。

我有一個微信公衆號,常常會分享一些python技術相關的乾貨;若是你喜歡個人分享,能夠用微信搜索「python語言學習」關注

歡迎你們加入千人交流答疑裙:699+749+852

相關文章
相關標籤/搜索