Python裝飾器

很多寫裝飾器的都是直接甩給你最終的裝飾器代碼,然后給你說(shuō)下大致的原理,比如:

#現(xiàn)在,假設(shè)我們要增強(qiáng)now()函數(shù)的功能漫试,比如浸锨,在函數(shù)調(diào)用前后自動(dòng)打印日志彻秆,但又不希望修改now()函數(shù)的定義柬泽,這種在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)框喳。
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
#觀察上面的log课幕,因?yàn)樗且粋€(gè)decorator,所以接受一個(gè)函數(shù)作為參數(shù)五垮,并返回一個(gè)函數(shù)乍惊。我們要借助Python的@語(yǔ)法,把decorator置于函數(shù)的定義處:
@log
def now():
    print('hello world')
#調(diào)用now()函數(shù)拼余,不僅會(huì)運(yùn)行now()函數(shù)本身污桦,還會(huì)在運(yùn)行now()函數(shù)前打印一行日志:    
>>> now()
call now():
hello world

然后來(lái)上一句,把@log放到now()函數(shù)的定義處亩歹,相當(dāng)于執(zhí)行了語(yǔ)句:

now = log(now)

對(duì)裝飾器只是大致的解釋了下原理,但是對(duì)于初學(xué)者根本就是懵逼的狀態(tài)

我認(rèn)為任何事都是一個(gè)復(fù)雜的組合體,就跟數(shù)學(xué)一樣,一個(gè)復(fù)雜的題是有很多簡(jiǎn)單的題組成的,化繁為簡(jiǎn)就可以很清晰的理解事情的本質(zhì)

下面讓我們把裝飾器拆分來(lái)看

1.函數(shù)/函數(shù)執(zhí)行

首先區(qū)分函數(shù)和函數(shù)的執(zhí)行

def foo():
    print('foo')

foo     #表示是函數(shù)
foo()   #表示執(zhí)行foo函數(shù)

2.函數(shù)指針

def foo():
    print('foo')

foo = lambda x: x + 1

foo()# 執(zhí)行下面的lambda表達(dá)式匙监,而不再是原來(lái)的foo函數(shù),因?yàn)閒oo這個(gè)名字被重新指向了另外一個(gè)匿名函數(shù)

3.最減版裝飾器

def w1(func):
    def inner():
        func()
    return inner

@w1
def f1():
    print('f1')

這段代碼不清楚沒(méi)關(guān)系,讓我們轉(zhuǎn)成另外一段,因?yàn)锧w1 等價(jià)于 w1(f1)

def w1(func):
    def inner():
        func()
    return inner

def f1():
    print('f1')

f1 = w1(f1)

如果你還是不清楚,那讓我們來(lái)一步步解釋下

執(zhí)行w1函數(shù) 小作,并將 f1 作為w1函數(shù)的參數(shù)亭姥,內(nèi)部就會(huì)回返inner(由1.函數(shù)/函數(shù)執(zhí)行應(yīng)該知道這是函數(shù)),即將w1的返回值再重新賦值給 f1,即:

新f1 = def inner(): 
            想要添加的方法內(nèi)容
            原來(lái)f1()
        return inner

當(dāng)執(zhí)行f1時(shí),就會(huì)調(diào)用inner函數(shù),先執(zhí)行你想要添加的方法內(nèi)容,然后再執(zhí)行原有的f1方法

再看下最減版的裝飾器了,看看理解了嗎.

4.用裝飾器給方法添加點(diǎn)內(nèi)容

def addStr(fn):
    def wrapped():
        return '添加的內(nèi)容:' + fn()
    return wrapped

@addStr
def test1()
    return 'hello world'

print(test1())

運(yùn)行結(jié)果:

添加內(nèi)容:hello world

5.裝飾器(decorator)功能

  • 引入日志
  • 函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì)
  • 執(zhí)行函數(shù)前預(yù)備處理
  • 執(zhí)行函數(shù)后清理功能
  • 權(quán)限校驗(yàn)等場(chǎng)景
  • 緩存

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

上邊是裝飾無(wú)參數(shù)的函數(shù),下邊讓我們看下裝飾有參數(shù)的函數(shù)

def test(func):
    def wrappedfunc(a, b):
        print(a, b)
        func(a, b)
    return wrappedfunc

@test
def foo(a, b):
    print(a+b)
foo(1,2)

7.裝飾不定長(zhǎng)參數(shù)的函數(shù)

def test(func):
    def wrappedfunc(*args, **kwargs):
        print(args, kwargs)
        func(*args, **kwargs)
    return wrappedfunc

@test
def foo1(a, b):
    print(a+b)
@test
def foo2(a, b, c):
    print(a+b+c)
    
foo1(1,2)
foo2(1,2,3)

對(duì)于不定長(zhǎng)參數(shù)的函數(shù)參數(shù)可以通過(guò)(args, kwargs)來(lái)修飾,args代表tuple(元組),kwargs代表dict(字典),這樣裝飾器就可以修飾不同參數(shù)長(zhǎng)度的函數(shù)

8.裝飾帶有return的函數(shù)

def test(func):
    def wrappedfunc():
        return func()
    return wrappedfunc

@test
def foo1():
    return 'haha'
@test
def foo2():
    print('----foo2----')

foo2()
print(foo1())

運(yùn)行可以知道兩個(gè)函數(shù)都可以正常運(yùn)行,因?yàn)閒oo2沒(méi)有return的,在wrappedfunc中return的是None,不會(huì)影響函數(shù)的正常執(zhí)行,而foo1中有return的也可以通過(guò)wrappedfunc中的return返回回來(lái).

9.通用裝飾器

這里就回到了開(kāi)頭大家普遍看到的通用裝飾器

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('hello world')

這時(shí)候看這個(gè)裝飾器,是不是感覺(jué)明白多了func.name就是打印函數(shù)名的一個(gè)方法

10.裝飾器帶參數(shù),在原有裝飾器的基礎(chǔ)上,設(shè)置外部變量

def test(pre="hello"):
    def testfun(func):
        def wrappedfunc():
            print(pre)
            return func()
        return wrappedfunc
    return testfun

@test("python")
def foo():
    print("I am foo")

可以理解為:foo()=test("python")(foo)()

11.類裝飾器

裝飾器函數(shù)其實(shí)是這樣一個(gè)接口約束顾稀,它必須接受一個(gè)callable對(duì)象作為參數(shù)达罗,然后返回一個(gè)callable對(duì)象。在Python中一般callable對(duì)象都是函數(shù)静秆,但也有例外粮揉。只要某個(gè)對(duì)象重寫了 call() 方法,那么這個(gè)對(duì)象就是callable的抚笔。

class Test(object):
    def __init__(self, func):
        print("---初始化---")
        print("func name is %s"%func.__name__)
        self.__func = func
    def __call__(self):
        print("---裝飾器中的功能---")
        self.__func()
#說(shuō)明:
#1. 當(dāng)用Test來(lái)裝作裝飾器對(duì)test函數(shù)進(jìn)行裝飾的時(shí)候扶认,首先會(huì)創(chuàng)建Test的實(shí)例對(duì)象
#    并且會(huì)把test這個(gè)函數(shù)名當(dāng)做參數(shù)傳遞到__init__方法中
#    即在__init__方法中的func變量指向了test函數(shù)體
#
#2. test函數(shù)相當(dāng)于指向了用Test創(chuàng)建出來(lái)的實(shí)例對(duì)象
#
#3. 當(dāng)在使用test()進(jìn)行調(diào)用時(shí),就相當(dāng)于讓這個(gè)對(duì)象()殊橙,因此會(huì)調(diào)用這個(gè)對(duì)象的__call__方法
#
#4. 為了能夠在__call__方法中調(diào)用原來(lái)test指向的函數(shù)體辐宾,所以在__init__方法中就需要一個(gè)實(shí)例屬性來(lái)保存這個(gè)函數(shù)體的引用
#    所以才有了self.__func = func這句代碼狱从,從而在調(diào)用__call__方法中能夠調(diào)用到test之前的函數(shù)體
@Test
def test():
    print("----test---")
test()
showpy()#如果把這句話注釋,重新運(yùn)行程序叠纹,依然會(huì)看到"--初始化--"

運(yùn)行結(jié)果如下

---初始化---
func name is test
---裝飾器中的功能---
----test---

我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末季研,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子誉察,更是在濱河造成了極大的恐慌与涡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件持偏,死亡現(xiàn)場(chǎng)離奇詭異递沪,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)综液,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門款慨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人谬莹,你說(shuō)我怎么就攤上這事檩奠。” “怎么了附帽?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵埠戳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蕉扮,道長(zhǎng)整胃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任喳钟,我火速辦了婚禮屁使,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奔则。我一直安慰自己蛮寂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布易茬。 她就那樣靜靜地躺著酬蹋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抽莱。 梳的紋絲不亂的頭發(fā)上范抓,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音食铐,去河邊找鬼匕垫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛璃岳,可吹牛的內(nèi)容都是我干的年缎。 我是一名探鬼主播悔捶,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼单芜!你這毒婦竟也來(lái)了蜕该?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤洲鸠,失蹤者是張志新(化名)和其女友劉穎堂淡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扒腕,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绢淀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瘾腰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皆的。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蹋盆,靈堂內(nèi)的尸體忽然破棺而出费薄,到底是詐尸還是另有隱情,我是刑警寧澤栖雾,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布楞抡,位于F島的核電站,受9級(jí)特大地震影響析藕,放射性物質(zhì)發(fā)生泄漏召廷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一账胧、第九天 我趴在偏房一處隱蔽的房頂上張望竞慢。 院中可真熱鬧,春花似錦找爱、人聲如沸梗顺。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至仑鸥,卻和暖如春吮播,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背眼俊。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工意狠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疮胖。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓环戈,卻偏偏與公主長(zhǎng)得像闷板,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子院塞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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

  • Python裝飾器的高級(jí)用法(翻譯) 原文地址https://www.codementor.io/python/t...
    城南道閱讀 4,813評(píng)論 1 22
  • 呵呵遮晚!作為一名教python的老師,我發(fā)現(xiàn)學(xué)生們基本上一開(kāi)始很難搞定python的裝飾器拦止,也許因?yàn)檠b飾器確實(shí)很難懂...
    TypingQuietly閱讀 19,552評(píng)論 26 186
  • 原文出處: dzone 譯文出處:Wu Cheng(@nullRef) 1. 函數(shù) 在python中县遣,函數(shù)通過(guò)...
    DraculaWong閱讀 521評(píng)論 0 3
  • 每個(gè)人都有的內(nèi)褲主要功能是用來(lái)遮羞,但是到了冬天它沒(méi)法為我們防風(fēng)御寒汹族,咋辦萧求?我們想到的一個(gè)辦法就是把內(nèi)褲改造一下,...
    chen_000閱讀 1,364評(píng)論 0 3
  • 最后一個(gè)畫歪了邊顶瞒,于是就多畫了幾個(gè)夸政。顏色涂的有點(diǎn)重口。
    我是小魚干呀閱讀 199評(píng)論 1 1