理解Python中裝飾器最佳方法~

了解裝飾器之前睁蕾,
可以先了解一下什么是閉包的概念為好:


閉包,
是指在一個函數(shù)中定義了一個另外一個函數(shù)桶至,內(nèi)函數(shù)里運(yùn)用了外函數(shù)的臨時變量(實(shí)際參數(shù)也是臨時變量)具壮,并且外函數(shù)的返回值是內(nèi)函數(shù)的引用(一切皆引用,所有的函數(shù)名字都只是函數(shù)體在內(nèi)存空間的一個引用馆类。)

通俗的解釋:在一個外函數(shù)中定義了一個內(nèi)函數(shù)混聊,內(nèi)函數(shù)里運(yùn)用了外函數(shù)的臨時變量弹谁,并且外函數(shù)的返回值是內(nèi)函數(shù)的引用乾巧。這樣就構(gòu)成了一個閉包。

一般情況下预愤,在我們認(rèn)知當(dāng)中沟于,如果一個函數(shù)結(jié)束,函數(shù)的內(nèi)部所有東西都會釋放掉植康,還給內(nèi)存旷太,局部變量都會消失。但是閉包是一種特殊情況,如果外函數(shù)在結(jié)束的時候發(fā)現(xiàn)有自己的臨時變量將來會在內(nèi)部函數(shù)中用到供璧,就把這個臨時變量綁定給了內(nèi)部函數(shù)存崖,然后自己再結(jié)束。

閉包的作用:
可以隱藏內(nèi)部函數(shù)的工作細(xì)節(jié)睡毒,只給外部使用者提供一個可以執(zhí)行的內(nèi)部函數(shù)的引用来惧。避免了使用全局變量,保證了程序的封裝性保證了內(nèi)函數(shù)的安全性演顾,其他函數(shù)不能訪問


裝飾器

裝飾器就是用于拓展已有函數(shù)功能的一種函數(shù)供搀,這個函數(shù)的特殊之處在于它的返回值也是一個函數(shù),實(shí)際上就是利用閉包語法實(shí)現(xiàn)的钠至。

裝飾器的作用

在不用更改原函數(shù)的代碼前提下給函數(shù)增加新的功能葛虐。


嗯嗯嗯嗯……然后,就閱讀了這么一堆:

詳解Python的裝飾器

理解 Python 裝飾器

python裝飾器簡介---這一篇也許就夠了

python 裝飾器 一篇就能講清楚

https://lotabout.me/2017/Python-Decorator/

Python裝飾器的前世今生

巴拉巴拉巴拉大概意思就是棉钧,這個很重要屿脐,需要多理解~

我閱讀了這么多文章的唯一的好處(感慨)就是,就算沒理解宪卿,概念和基本使用也強(qiáng)行記住了

但是理解沒摄悯?
——不知道,

@⒉丁I菅薄!4位妗瘪阁!最后最后,只想安利:

最好的理解辦法就是邮偎,先寫一個簡單的例子管跺,然后,跟著調(diào)試器斷點(diǎn)一步一步走禾进,
然后多寫點(diǎn)其他的情況豁跑,斷點(diǎn)走走就知道其中的運(yùn)行機(jī)制了
最后跟著概念也就理解了

def decorator(fun):
    print('I am in decorator!')

    print('will do fun')
    fun()
    print('goodbye fun')

    def inner():
        print('I am come in inner')
        fun()
        print('inner will say goodbye')
    
    #測試是否運(yùn)行test2
    def test2():
        print('I am in test2')
        fun()


    return inner

print('test')

@decorator
def outfun():
    print('I am in outfun')

print('will do outfun')
outfun()

print('\nIt will do decorator')
decorator(outfun)

輸出:

test
I am in decorator!
will do fun
I am in outfun
goodbye fun
will do outfun
I am come in inner
I am in outfun
inner will say goodbye

It will do decorator
I am in decorator!
will do fun
I am come in inner
I am in outfun
inner will say goodbye
goodbye fun

當(dāng)然,關(guān)于裝飾器還有很多知識點(diǎn)泻云,遠(yuǎn)不止上面的測試程序這一點(diǎn)艇拍,詳細(xì)了解還是可以看看上面的鏈接文章的。

如宠纯,

對有參函數(shù)進(jìn)行裝飾

對帶返回值的函數(shù)進(jìn)行裝飾

帶參數(shù)的裝飾器

類裝飾器

通用裝飾器

內(nèi)置的裝飾器設(shè)置
……


下面的文字轉(zhuǎn)載:python裝飾器簡介

對有參函數(shù)進(jìn)行裝飾

在使用中卸夕,有的函數(shù)可能會帶有參數(shù),那么這種如何處理呢婆瓜?
代碼優(yōu)先:

def w_say(fun):
    """
    如果原函數(shù)有參數(shù)快集,那閉包函數(shù)必須保持參數(shù)個數(shù)一致贡羔,并且將參數(shù)傳遞給原方法
    """

    def inner(name):
        """
        如果被裝飾的函數(shù)有行參,那么閉包函數(shù)必須有參數(shù)
        :param name:
        :return:
        """
        print('say inner called')
        fun(name)

    return inner


@w_say
def hello(name):
    print('hello ' + name)


hello('wangcai')

輸出為:

say inner called
hello wangcai

此時个初,也許你就會問了乖寒,那是一個參數(shù)的,如果多個或者不定長參數(shù)呢院溺,該如何處理呢宵统?看看下面的代碼你就秒懂了。

def w_add(func):
    def inner(*args, **kwargs):
        print('add inner called')
        func(*args, **kwargs)

    return inner


@w_add
def add(a, b):
    print('%d + %d = %d' % (a, b, a + b))


@w_add
def add2(a, b, c):
    print('%d + %d + %d = %d' % (a, b, c, a + b + c))


add(2, 4)
add2(2, 4, 6)

輸出結(jié)果為:

add inner called
2 + 4 = 6
add inner called
2 + 4 + 6 = 12

對帶返回值的函數(shù)進(jìn)行裝飾

下面對有返回值的函數(shù)進(jìn)行裝飾覆获,按照之前的寫法马澈,代碼是這樣的

def w_test(func):
    def inner():
        print('w_test inner called start')
        func()
        print('w_test inner called end')
    return inner


@w_test
def test():
    print('this is test fun')
    return 'hello'


ret = test()
print('ret value is %s' % ret)

輸出結(jié)果為

w_test inner called start
this is test fun
w_test inner called end
ret value is None

可以發(fā)現(xiàn),此時弄息,并沒有輸出test函數(shù)的‘hello’,而是None痊班,那是為什么呢,可以發(fā)現(xiàn)摹量,在inner函數(shù)中對test進(jìn)行了調(diào)用涤伐,但是沒有接受不了返回值,也沒有進(jìn)行返回缨称,那么默認(rèn)就是None了凝果,知道了原因,那么來修改一下代碼:

def w_test(func):
    def inner():
        print('w_test inner called start')
        str = func()
        print('w_test inner called end')
        return str

    return inner


@w_test
def test():
    print('this is test fun')
    return 'hello'


ret = test()
print('ret value is %s' % ret)

結(jié)果輸出為:

w_test inner called start
this is test fun
w_test inner called end
ret value is hello

帶參數(shù)的裝飾器

介紹了對帶參數(shù)的函數(shù)和有返回值的函數(shù)進(jìn)行裝飾睦尽,那么有沒有帶參數(shù)的裝飾器呢器净,如果有的話,又有什么用呢当凡?
答案肯定是有的山害,接下來通過代碼來看一下吧。

def func_args(pre='xiaoqiang'):
    def w_test_log(func):
        def inner():
            print('...記錄日志...visitor is %s' % pre)
            func()

        return inner

    return w_test_log


# 帶有參數(shù)的裝飾器能夠起到在運(yùn)行時沿量,有不同的功能

# 先執(zhí)行func_args('wangcai')浪慌,返回w_test_log函數(shù)的引用
# @w_test_log
# 使用@w_test_log對test_log進(jìn)行裝飾
@func_args('wangcai')
def test_log():
    print('this is test log')


test_log()

輸出結(jié)果為:

...記錄日志...visitor is wangcai
this is test log

簡單理解,帶參數(shù)的裝飾器就是在原閉包的基礎(chǔ)上又加了一層閉包朴则,通過外層函數(shù)func_args的返回值w_test_log就看出來了权纤,具體執(zhí)行流程在注釋里已經(jīng)說明了。
好處就是可以在運(yùn)行時乌妒,針對不同的參數(shù)做不同的應(yīng)用功能處理汹想。

通用裝飾器
介紹了這么多,在實(shí)際應(yīng)用中芥被,如果針對沒個類別的函數(shù)都要寫一個裝飾器的話欧宜,估計(jì)就累死了,那么有沒有通用萬能裝飾器呢拴魄,答案肯定是有的,廢話不多說,直接上代碼匹中。

def w_test(func):
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret

    return inner


@w_test
def test():
    print('test called')


@w_test
def test1():
    print('test1 called')
    return 'python'


@w_test
def test2(a):
    print('test2 called and value is %d ' % a)


test()
test1()
test2(9)

輸出為:

test called
test1 called
test2 called and value is 9 

類裝飾器

裝飾器函數(shù)其實(shí)是一個接口約束夏漱,它必須接受一個callable對象作為參數(shù),然后返回一個callable對象顶捷。

當(dāng)創(chuàng)建一個對象后挂绰,直接去執(zhí)行這個對象,那么是會拋出異常的服赎,因?yàn)樗皇莄allable葵蒂,無法直接執(zhí)行,但進(jìn)行修改后重虑,就可以直接執(zhí)行調(diào)用了践付,如下

class Test(object):
    def __call__(self, *args, **kwargs):
        print('call called')


t = Test()
print(t())

輸出為:

call called

下面,引入正題缺厉,看一下如何用類裝飾函數(shù)永高。

class Test(object):
    def __init__(self, func):
        print('test init')
        print('func name is %s ' % func.__name__)
        self.__func = func

    def __call__(self, *args, **kwargs):
        print('裝飾器中的功能')
        self.__func()


@Test
def test():
    print('this is test func')


test()

輸出結(jié)果為:

test init
func name is test 
裝飾器中的功能
this is test func

和之前的原理一樣,當(dāng)python解釋器執(zhí)行到到@Test時提针,會把當(dāng)前test函數(shù)作為參數(shù)傳入Test對象,調(diào)用init方法,同時將test函數(shù)指向創(chuàng)建的Test對象舱殿,那么在接下來執(zhí)行test()的時候僵娃,其實(shí)就是直接對創(chuàng)建的對象進(jìn)行調(diào)用,執(zhí)行其call方法嗜价。


關(guān)于裝飾器里的上古神器:

https://zhangchuzhao.site/2018/05/25/python-decorator/

  1. @property -> getter/setter方法
  2. @classmethod落萎、@staticmethod
  3. @functools.wraps
  4. Easter egg

另外,
關(guān)于迭代器和生成器可參考下面的優(yōu)質(zhì)文章

完全理解Python迭代對象炭剪、迭代器练链、生成器
Python迭代器與生成器


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奴拦,隨后出現(xiàn)的幾起案子媒鼓,更是在濱河造成了極大的恐慌,老刑警劉巖错妖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绿鸣,死亡現(xiàn)場離奇詭異,居然都是意外死亡暂氯,警方通過查閱死者的電腦和手機(jī)潮模,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痴施,“玉大人擎厢,你說我怎么就攤上這事究流。” “怎么了动遭?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵芬探,是天一觀的道長。 經(jīng)常有香客問我厘惦,道長偷仿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任宵蕉,我火速辦了婚禮酝静,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘羡玛。我一直安慰自己别智,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布缝左。 她就那樣靜靜地躺著亿遂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渺杉。 梳的紋絲不亂的頭發(fā)上蛇数,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音是越,去河邊找鬼耳舅。 笑死,一個胖子當(dāng)著我的面吹牛倚评,可吹牛的內(nèi)容都是我干的浦徊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼天梧,長吁一口氣:“原來是場噩夢啊……” “哼盔性!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呢岗,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤冕香,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后后豫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悉尾,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年挫酿,在試婚紗的時候發(fā)現(xiàn)自己被綠了构眯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡早龟,死狀恐怖惫霸,靈堂內(nèi)的尸體忽然破棺而出猫缭,到底是詐尸還是另有隱情,我是刑警寧澤它褪,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布饵骨,位于F島的核電站翘悉,受9級特大地震影響茫打,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妖混,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一老赤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧制市,春花似錦抬旺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至误褪,卻和暖如春责鳍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兽间。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工历葛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘀略。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓恤溶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親帜羊。 傳聞我的和親對象是個殘疾皇子咒程,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345