令人頭大的裝飾器(1)

一個筆試經歷,是一道關于裝飾器的題目,那時候的感覺應該是這樣:

  • " 美鋁,我們是不是認識? "

俗話說得好:一回生二回熟。但學習裝飾器時只是了解下,同時久不使用這方面的知識,好刀都要變鈍呀,況且自己拿的還是把破刀。被刺激的不要不要的 ~

首先,遇到一個云里霧里的問題:

1. 什么是裝飾器?

第一要明確知道的概念:裝飾器屬于Python的高階函數(聽說還是最常用到的高階函數)。兩個重點:

  1. 它是一個函數
  2. 它是一個感覺起來很高級的函數

所以,【劃重點】裝飾器是一個語法糖,它以一個函數為參數,并返回一個加強版的原函數(方法或者類)對象。當然,這個裝飾器要設計正確。

有點口語化,那就摘抄一段正式點的概念:

裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用。

enmm~那裝飾器的作用就是

在不改動函數的情況下,給函數或者對象(其實函數也是一個對象)在運行時動態地額外添加其他的功能。

那么就進入第二內容,寫一點代碼出來溜溜,來理解下令人無趣的概念

2. 入門級別的裝飾器(面試題就是入門級的,我好想哭)

別嗶嗶,直接上代碼:

# 有個單純要去看電影的函數,就叫 movie() 吧,但你又想邊看電影邊吃爆米花。用裝飾器如何寫呢?

def extra(func):
    """給 movie()添加額外的功能"""
    def wrapper():
        print("我要先把爆米花和可樂買了!")
        return func()  # 得在加強版原函數里調用原函數。給自己穿漂亮的衣服可不要把自己本身忘記了。
    return wrapper

@extra
def movie():
    print("我要看電影。")
              
movie()

-----------------------------這是運行結果線----------------------------------------

我要先把爆米花和可樂買了!
我要看電影。

extra() 函數就是一個裝飾器。(兩點分析)

  1. 在 extra 函數中傳入的 func參數是一個函數,即 movie()這個函數。符合高階函數的身份。在以前沒有加入語法糖時,@extra 等價于 movie = extra( movie )。這里涉及到一個知識點:

函數是一個對象,函數對象還可以賦值給變量。所以可以通過變量調用這個函數。

def cat():
    print('This is a ugly cat!')

c = cat # 將函數對象賦值給變量c
c()        

其實,Python 里所有數據——布爾值、整數、浮點數、字符串,甚至大型數據結構、函數以及程序——都是以對象(object)的形式存在的。
扯遠了,回歸正題——

  1. return wrapper 中的 wrapper 就是加強版的 movie函數。這個加強版的movie函數除了有看電影還有了要買爆米花和可樂的功能。符合返回一個加強版的原函數的概念。

但是——
如果這個被裝飾器裝飾的函數 moive需要傳入一個電影名name的參數。那么這個加強版函數wrapper和其里面的函數func也要做相應的更改。但 return wrapper中的wrapper不需要更改,因裝飾器返回的是一個對象。是不需要添加參數的。

def extra(func):
    """給 movie()添加額外的功能"""
    def wrapper(name):   # 一毛一樣的參數
        print("我要先把爆米花和可樂買了!")
        return func(name)
    return wrapper

@extra
def movie(name):
    print("我要看《{}》電影。".format(name))
              
movie('蜘蛛俠 英雄歸來')
----------------迷人的結果線------------------

我要先把爆米花和可樂買了!
我要看《蜘蛛俠 英雄歸來》電影。

雖然把裝飾器的里的加強版函數的參數弄的與原函數一毛一樣,但這裝飾器是一種封裝,如果萬一你想看一部,而其他人想看兩部呢?難道又給另個人寫個相似的裝飾器嗎?所以讓裝飾器同步更改參數的做法是不可取的。好辦法就是給裝飾器一個無敵的參數,一個可變參數*args和關鍵字參數**kwargs

def extra(func):
    """給 movie()添加額外的功能"""
    def wrapper(*args, **kwagrs):   # 無敵的參數了吧
        print("我要先把爆米花和可樂買了!")
        return func(*args, **kwagrs)
    return wrapper

@extra
def movie1(name):
    print("我要看《{}》電影。".format(name))
    
@extra
def movie2(name1, name2):
    print("我要看兩部電影《{0}》和《{1}》".format(name1, name2))
              
movie1('蜘蛛俠 英雄歸來')
print('\n')
movie2('蜘蛛俠 英雄歸來','猩球崛起')

嘻嘻,好累呀!那么入門的就結束了,進入進階版本的裝飾器了。發車了~~~吱吱吱~~~
還是下一篇再寫吧。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容