Python 筆記 | 從零開始,理解python裝飾器

還是決定寫一篇關(guān)于python裝飾器的文章. 裝飾器實(shí)在是太常用,也太好用的東西了. 這篇文章會從函數(shù)開始, 一步步解釋裝飾器. 因?yàn)檠b飾器實(shí)際是也只是高階函數(shù)而已.

1. 函數(shù)

函數(shù)就比較基礎(chǔ)了, 就像一個黑箱, 傳入?yún)?shù), 出來返回值. 比如這樣

>>> def incr(num):
...     return num + 1
... 
>>> incr(21)
22

2. 函數(shù)內(nèi)部定義函數(shù)

python支持在函數(shù)內(nèi)部定義函數(shù), 比如:

def parent():
    print("Printing from the parent() function")
    def first_child():
        print("Printing from the first_child() function")
    def second_child():
        print("Printing from the second_child() function")
    second_child()
    first_child()

這個也很好理解, 輸出結(jié)果是這樣的:

>>> parent()
Printing from the parent() function
Printing from the second_child() function
Printing from the first_child() function

在函數(shù)內(nèi)部定義的函數(shù), 作用域只在函數(shù)內(nèi)部. 當(dāng)然, 我們可以再來個復(fù)雜點(diǎn)的例子.

>>> def hello(name):
...     def say_hello():
...         print("Hello", name)
...     say_hello()
... 
>>> hello('world')
Hello world

注意, 這里在hello函數(shù)里面定義了一個函數(shù) say_hello , say_hello這里面用到一個變量name, 是函數(shù)hello傳入的參數(shù), 這個在裝飾器中會用到. 大家自己理解下.

3. 一切皆對象

python中一切皆是對象, 所有的類, 生成器, 變量, 甚至函數(shù)也是一樣. 比如我們上面定義的incr函數(shù):

>>> a = incr
>>> a
<function incr at 0x7f5485f77050>
>>> a(7)
8
>>> 

那既然是對象, 就可以作為參數(shù)傳進(jìn)函數(shù)中, 比如說:

>>> def print_incr(fun, num):
...     num = fun(num)
...     print(num+10)
>>> print_incr(incr, 10)
21

同樣的道理, 函數(shù)既然可以作為參數(shù), 也可以作為返回值, 比如:


>>> def left_or_right(direction):
... 
...     def left():
...         print('<-')
... 
...     def right():
...         print('->')
... 
...     if direction == 'left':
...         return left
...
...     elif direction == 'right':
...         return right
... 
>>> left_or_right('left')
<function left at 0x7f5485f73cb0>

>>> fun = left_or_right('left')

>>> fun()
<-

4. 簡單的裝飾器

裝飾器聽得很多了, 那到底裝飾器是個什么東西? 聽起來這么高端? 其實(shí)很簡單, 我們玩?zhèn)€補(bǔ)全句子的文字游戲, 括號括起來的是定語

  1. 裝飾器是函數(shù).
  2. 裝飾器是 (參數(shù)是函數(shù), 返回值也是函數(shù)的) 函數(shù).

寫裝飾器的時候大家請一定牢記這兩句
那么我們開始寫第一個裝飾器了.

def decorator(func):
   # 我們在函數(shù)內(nèi)部定義一個函數(shù)
   def wrapper():
        print('start')
        # 這里調(diào)用一下我們傳進(jìn)來的函數(shù)
        func()
        print('end')
   #  返回一個函數(shù)
   return wrapper

好了, 這就是一個簡單的裝飾器了. 我們可以這樣去使用這個裝飾器.

>>> def hello():
...     print('hello')
... 
>>> say_hello = decorator(hello)
>>> say_hello
<function decorator.<locals>.wrapper at 0x7f5485f4df80>
>>> say_hello()
start
hello
end
>>> 

這里的decorator就是裝飾器了, 傳入一個hello函數(shù), 返回一個新的函數(shù), 所以歸根到底就是個函數(shù). 一切皆對象, 所以返回函數(shù)和返回?cái)?shù)字, 返回類的實(shí)例, 都沒什么太大區(qū)別.
如果你認(rèn)真閱讀前面三個部分, 這里的裝飾器應(yīng)該沒有任何問題. 如果還是感覺到困惑, 再去理解一遍前面三點(diǎn).

5. 語法糖

如果我們做個這樣一個裝飾器, 每次調(diào)用都要調(diào)用一下, 這可不太優(yōu)雅, 于是python提供一種便捷的語法糖方式. 還是上面的這個裝飾器舉例子

def decorator(func):
   # 我們在函數(shù)內(nèi)部定義一個函數(shù)
   def wrapper():
        print('start')
        # 這里調(diào)用一下我們傳進(jìn)來的函數(shù)
        func()
        print('end')
   #  返回一個函數(shù)
   return wrapper

@decorator
def hello():
    print('hello')

這時候我們再調(diào)用hello

>>> hello()
start
hello
end

所以, @decorator 的意思就是說, hello = decorator(hello) , 是不是裝飾器也沒有那么復(fù)雜?

6. 帶參數(shù)的函數(shù)的裝飾器

動手試一下, 如何寫這樣一個函數(shù)的裝飾器?

def add(a, b):
    return a+b

如果你對前面的部分理解的比較清楚透徹, 你大概已經(jīng)實(shí)現(xiàn)出來了.

我們理清一下思路, 裝飾器就是一個函數(shù), 傳入函數(shù), 返回函數(shù), 那就是這樣子了

def decorator(fun):
    def wrapper(a, b):
        print('start')
        t = fun(a,b)
        print(t)
        print('end')
    return wrapper

>>> a = decorator(add)
>>> a
<function decorator.<locals>.wrapper at 0x7f5485f4d680>
>>> a(7, 8)的
start
15
end

7. 可帶參數(shù)的裝飾器

我們這里實(shí)現(xiàn)一個裝飾器功能:
給定兩個參數(shù), msg(string) 和 times(int), 對于被裝飾的函數(shù), 先輸出msg信息, 然后將fun運(yùn)行times次,

同理, 如果你對于前面理解的透徹, 這部分應(yīng)該寫出來了.

給點(diǎn)提示, 裝飾器是什么?
裝飾器是函數(shù), 參數(shù)是函數(shù), 返回值也是函數(shù).

那么我們的裝飾器能不能用別的函數(shù)返回回來呢? 當(dāng)然可以.


#  外面這個函數(shù)其實(shí)是用來接受參數(shù)的外殼
def deco(msg, times):
    # 里面的這個函數(shù)其實(shí)才是我們最終的裝飾器
    def wrapper(fun):
        print(msg)
        for i in range(times):   
            fun()
    # 這里返回我們的裝飾器
    return wrapper

>>> @deco(time=5) 
... def hello(): 
...     print('hello world') 
...                
                                                                                                                                                                                             
hello world
hello world
hello world
hello world
hello world

8. 舉個例子

計(jì)數(shù)裝飾器: 計(jì)算函數(shù)運(yùn)行次數(shù)

import functools

def count_calls(func):
    @functools.wraps(func)
    def wrapper_count_calls(*args, **kwargs):
        wrapper_count_calls.num_calls += 1
        print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")
        return func(*args, **kwargs)
    wrapper_count_calls.num_calls = 0
    return wrapper_count_calls

@count_calls
def say_whee():
    print("Whee!")

9. 寫在后面

裝飾器的用途實(shí)在很多很廣, 如果有興趣進(jìn)一步了解, 可以去python的裝飾器庫去看一下:
https://wiki.python.org/moin/PythonDecoratorLibrary

我一開始用裝飾器的時候也是一臉懵逼, 但是也堅(jiān)持去用, 只要能用到的地方, 都盡可能的去使用, 不會的地方就去查別人的代碼, 然后模仿著去寫, 慢慢會用了之后再回頭咀嚼原理直到吃透. 所以, 最重要的是多動手, 寫就完了.

寫了蠻久的, 如果對你有幫助, 順手點(diǎn)個贊(≧▽≦)/. 大佬們贊賞就更歡迎啦. 還在學(xué)習(xí), 如果有哪里不對的請多指教.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涂屁,一起剝皮案震驚了整個濱河市蟀悦,隨后出現(xiàn)的幾起案子沟于,更是在濱河造成了極大的恐慌噪窘,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泞遗,死亡現(xiàn)場離奇詭異惰许,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)史辙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門汹买,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佩伤,“玉大人,你說我怎么就攤上這事晦毙∩玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵见妒,是天一觀的道長孤荣。 經(jīng)常有香客問我,道長须揣,這世上最難降的妖魔是什么盐股? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮耻卡,結(jié)果婚禮上疯汁,老公的妹妹穿的比我還像新娘。我一直安慰自己卵酪,他們只是感情好幌蚊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凛澎,像睡著了一般霹肝。 火紅的嫁衣襯著肌膚如雪估蹄。 梳的紋絲不亂的頭發(fā)上塑煎,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機(jī)與錄音臭蚁,去河邊找鬼最铁。 笑死,一個胖子當(dāng)著我的面吹牛垮兑,可吹牛的內(nèi)容都是我干的冷尉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼系枪,長吁一口氣:“原來是場噩夢啊……” “哼雀哨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起私爷,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤雾棺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后衬浑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌浩,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年工秩,在試婚紗的時候發(fā)現(xiàn)自己被綠了尸饺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片进统。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浪听,靈堂內(nèi)的尸體忽然破棺而出螟碎,到底是詐尸還是另有隱情,我是刑警寧澤迹栓,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布抚芦,位于F島的核電站,受9級特大地震影響迈螟,放射性物質(zhì)發(fā)生泄漏叉抡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一答毫、第九天 我趴在偏房一處隱蔽的房頂上張望褥民。 院中可真熱鬧,春花似錦洗搂、人聲如沸消返。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撵颊。三九已至,卻和暖如春惫叛,著一層夾襖步出監(jiān)牢的瞬間倡勇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工嘉涌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妻熊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓仑最,卻偏偏與公主長得像扔役,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子警医,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 部分細(xì)節(jié)自己改了點(diǎn)亿胸,也加了點(diǎn)自己例子,基本上屬于轉(zhuǎn)載预皇。轉(zhuǎn)載出處:https://my.oschina.net/le...
    洛克黃瓜閱讀 1,976評論 0 3
  • 要點(diǎn): 函數(shù)式編程:注意不是“函數(shù)編程”侈玄,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍睢傩浴?..
    victorsungo閱讀 1,512評論 0 6
  • Python的裝飾器(decorator)是一個很棒的機(jī)制深啤,也是熟練運(yùn)用Python的必殺技之一拗馒。裝飾器,顧名思義...
    溫柔的傾訴閱讀 396評論 0 0
  • 一溯街、裝飾器的基本使用 在不改變函數(shù)源代碼的前提下诱桂,給函數(shù)添加新的功能洋丐,這時就需要用到“裝飾器”。 0.開放封閉原則...
    NJingZYuan閱讀 529評論 0 0
  • 在灰藍(lán)的宿舍里,住著四位性格迥異的小仙女肝劲,他們有著開朗樂觀的性子迁客,也有著溫柔體貼的時候。就這樣彼此和諧相處辞槐,雖然有...
    木莓橙閱讀 790評論 0 1