(2018-04-06.Python從Zero到One)一、python高級(jí)編程__1.1.7裝飾器

上一篇文章為:→1.1.6閉包

裝飾器

裝飾器是程序開(kāi)發(fā)中經(jīng)常會(huì)用到的一個(gè)功能彤灶,用好了裝飾器看幼,開(kāi)發(fā)效率如虎添翼,所以這也是Python面試中必問(wèn)的問(wèn)題幌陕,但對(duì)于好多初次接觸這個(gè)知識(shí)的人來(lái)講诵姜,這個(gè)功能有點(diǎn)繞,自學(xué)時(shí)直接繞過(guò)去了搏熄,然后面試問(wèn)到了就掛了棚唆,因?yàn)檠b飾器是程序開(kāi)發(fā)的基礎(chǔ)知識(shí),這個(gè)都不會(huì)搬卒,別跟人家說(shuō)你會(huì)Python, 看了下面的文章瑟俭,保證你學(xué)會(huì)裝飾器。

1契邀、先明白這段代碼

#### 第一波 ####
def foo():
    print('foo')

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

#### 第二波 ####
def foo():
    print('foo')

foo = lambda x: x + 1

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

2坯门、需求來(lái)了

初創(chuàng)公司有N個(gè)業(yè)務(wù)部門微饥,1個(gè)基礎(chǔ)平臺(tái)部門,基礎(chǔ)平臺(tái)負(fù)責(zé)提供底層的功能古戴,如:數(shù)據(jù)庫(kù)操作欠橘、redis調(diào)用、監(jiān)控API等功能现恼。業(yè)務(wù)部門使用基礎(chǔ)功能時(shí)肃续,只需調(diào)用基礎(chǔ)平臺(tái)提供的功能即可。如下:

############### 基礎(chǔ)平臺(tái)提供的功能如下 ###############

def f1():
    print('f1')

def f2():
    print('f2')

def f3():
    print('f3')

def f4():
    print('f4')

############### 業(yè)務(wù)部門A 調(diào)用基礎(chǔ)平臺(tái)提供的功能 ###############

f1()
f2()
f3()
f4()

############### 業(yè)務(wù)部門B 調(diào)用基礎(chǔ)平臺(tái)提供的功能 ###############

f1()
f2()
f3()
f4()

目前公司有條不紊的進(jìn)行著叉袍,但是始锚,以前基礎(chǔ)平臺(tái)的開(kāi)發(fā)人員在寫代碼時(shí)候沒(méi)有關(guān)注驗(yàn)證相關(guān)的問(wèn)題,即:基礎(chǔ)平臺(tái)的提供的功能可以被任何人使用≡洌現(xiàn)在需要對(duì)基礎(chǔ)平臺(tái)的所有功能進(jìn)行重構(gòu)瞧捌,為平臺(tái)提供的所有功能添加驗(yàn)證機(jī)制,即:執(zhí)行功能前润文,先進(jìn)行驗(yàn)證姐呐。

老大把工作交給 Low B,他是這么做的:

跟每個(gè)業(yè)務(wù)部門交涉典蝌,每個(gè)業(yè)務(wù)部門自己寫代碼曙砂,調(diào)用基礎(chǔ)平臺(tái)的功能之前先驗(yàn)證。誒骏掀,這樣一來(lái)基礎(chǔ)平臺(tái)就不需要做任何修改了调炬。太棒了,有充足的時(shí)間泡妹子...

當(dāng)天Low B 被開(kāi)除了…

老大把工作交給 Low BB米绕,他是這么做的:
############### 基礎(chǔ)平臺(tái)提供的功能如下 ############### 

def f1():
    # 驗(yàn)證1
    # 驗(yàn)證2
    # 驗(yàn)證3
    print('f1')

def f2():
    # 驗(yàn)證1
    # 驗(yàn)證2
    # 驗(yàn)證3
    print('f2')

def f3():
    # 驗(yàn)證1
    # 驗(yàn)證2
    # 驗(yàn)證3
    print('f3')

def f4():
    # 驗(yàn)證1
    # 驗(yàn)證2
    # 驗(yàn)證3
    print('f4')

############### 業(yè)務(wù)部門不變 ############### 
### 業(yè)務(wù)部門A 調(diào)用基礎(chǔ)平臺(tái)提供的功能### 

f1()
f2()
f3()
f4()

### 業(yè)務(wù)部門B 調(diào)用基礎(chǔ)平臺(tái)提供的功能 ### 

f1()
f2()
f3()
f4()

過(guò)了一周 Low BB 被開(kāi)除了…

老大把工作交給 Low BBB娇哆,他是這么做的:

只對(duì)基礎(chǔ)平臺(tái)的代碼進(jìn)行重構(gòu),其他業(yè)務(wù)部門無(wú)需做任何修改

############### 基礎(chǔ)平臺(tái)提供的功能如下 ############### 

def check_login():
    # 驗(yàn)證1
    # 驗(yàn)證2
    # 驗(yàn)證3
    pass


def f1():

    check_login()

    print('f1')

def f2():

    check_login()

    print('f2')

def f3():

    check_login()

    print('f3')

def f4():

    check_login()

    print('f4')

老大看了下Low BBB 的實(shí)現(xiàn)侧纯,嘴角漏出了一絲的欣慰的笑新锈,語(yǔ)重心長(zhǎng)的跟Low BBB聊了個(gè)天:

老大說(shuō):

寫代碼要遵循開(kāi)放封閉原則,雖然在這個(gè)原則是用的面向?qū)ο箝_(kāi)發(fā)眶熬,但是也適用于函數(shù)式編程妹笆,簡(jiǎn)單來(lái)說(shuō),它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改娜氏,但可以被擴(kuò)展拳缠,即:

封閉:已實(shí)現(xiàn)的功能代碼塊
開(kāi)放:對(duì)擴(kuò)展開(kāi)發(fā)
如果將開(kāi)放封閉原則應(yīng)用在上述需求中,那么就不允許在函數(shù) f1 贸弥、f2窟坐、f3、f4的內(nèi)部進(jìn)行修改代碼绵疲,老板就給了Low BBB一個(gè)實(shí)現(xiàn)方案:

def w1(func):
    def inner():
        # 驗(yàn)證1
        # 驗(yàn)證2
        # 驗(yàn)證3
        func()
    return inner

@w1
def f1():
    print('f1')
@w1
def f2():
    print('f2')
@w1
def f3():
    print('f3')
@w1
def f4():
    print('f4')

對(duì)于上述代碼哲鸳,也是僅僅對(duì)基礎(chǔ)平臺(tái)的代碼進(jìn)行修改,就可以實(shí)現(xiàn)在其他人調(diào)用函數(shù) f1 f2 f3 f4 之前都進(jìn)行【驗(yàn)證】操作盔憨,并且其他業(yè)務(wù)部門無(wú)需做任何操作徙菠。

Low BBB心驚膽戰(zhàn)的問(wèn)了下,這段代碼的內(nèi)部執(zhí)行原理是什么呢郁岩?

老大正要生氣婿奔,突然Low BBB的手機(jī)掉到地上,恰巧屏保就是Low BBB的女友照片问慎,老大一看一緊一抖萍摊,喜笑顏開(kāi),決定和Low BBB交個(gè)好朋友蝴乔。

詳細(xì)的開(kāi)始講解了:

單獨(dú)以f1為例:

def w1(func):
    def inner():
        # 驗(yàn)證1
        # 驗(yàn)證2
        # 驗(yàn)證3
        func()
    return inner

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

python解釋器就會(huì)從上到下解釋代碼记餐,步驟如下:

def w1(func): ==>將w1函數(shù)加載到內(nèi)存
@w1
沒(méi)錯(cuò), 從表面上看解釋器僅僅會(huì)解釋這兩句代碼薇正,因?yàn)楹瘮?shù)在 沒(méi)有被調(diào)用之前其內(nèi)部代碼不會(huì)被執(zhí)行片酝。

從表面上看解釋器著實(shí)會(huì)執(zhí)行這兩句,但是 @w1 這一句代碼里卻有大文章挖腰, @函數(shù)名 是python的一種語(yǔ)法糖雕沿。

上例@w1內(nèi)部會(huì)執(zhí)行一下操作:

執(zhí)行w1函數(shù)

執(zhí)行w1函數(shù) ,并將 @w1 下面的函數(shù)作為w1函數(shù)的參數(shù)猴仑,即:@w1 等價(jià)于 w1(f1) 所以审轮,內(nèi)部就會(huì)去執(zhí)行:

def inner(): 
    #驗(yàn)證 1
    #驗(yàn)證 2
    #驗(yàn)證 3
    f1()     # func是參數(shù)肥哎,此時(shí) func 等于 f1 
return inner# 返回的 inner,inner代表的是函數(shù)疾渣,非執(zhí)行函數(shù) ,其實(shí)就是將原來(lái)的 f1 函數(shù)塞進(jìn)另外一個(gè)函數(shù)中
w1的返回值

將執(zhí)行完的w1函數(shù)返回值 賦值 給@w1下面的函數(shù)的函數(shù)名f1 即將w1的返回值再重新賦值給 f1篡诽,即:

新f1 = def inner(): 
            #驗(yàn)證 1
            #驗(yàn)證 2
            #驗(yàn)證 3
            原來(lái)f1()
        return inner

所以,以后業(yè)務(wù)部門想要執(zhí)行 f1 函數(shù)時(shí)榴捡,就會(huì)執(zhí)行 新f1 函數(shù)杈女,在新f1 函數(shù)內(nèi)部先執(zhí)行驗(yàn)證,再執(zhí)行原來(lái)的f1函數(shù)吊圾,然后將原來(lái)f1 函數(shù)的返回值返回給了業(yè)務(wù)調(diào)用者达椰。

如此一來(lái), 即執(zhí)行了驗(yàn)證的功能项乒,又執(zhí)行了原來(lái)f1函數(shù)的內(nèi)容啰劲,并將原f1函數(shù)返回值 返回給業(yè)務(wù)調(diào)用著

Low BBB 你明白了嗎?要是沒(méi)明白的話檀何,我晚上去你家?guī)湍憬鉀Q吧S恪!埃碱!

3. 再議裝飾器

#定義函數(shù):完成包裹數(shù)據(jù)
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

#定義函數(shù):完成包裹數(shù)據(jù)
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test3():
    return "hello world-3"

print(test1()))
print(test2()))
print(test3()))

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

<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>

4. 裝飾器(decorator)功能

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

5. 裝飾器示例

例1:無(wú)參數(shù)的函數(shù)
from time import ctime, sleep

def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        func()
    return wrappedfunc

@timefun
def foo():
    print("I am foo")

foo()
sleep(2)
foo()

上面代碼理解裝飾器執(zhí)行行為可理解成

foo = timefun(foo)
#foo先作為參數(shù)賦值給func后,foo接收指向timefun返回的wrappedfunc
foo()
#調(diào)用foo(),即等價(jià)調(diào)用wrappedfunc()
#內(nèi)部函數(shù)wrappedfunc被引用猖辫,所以外部函數(shù)的func變量(自由變量)并沒(méi)有釋放
#func里保存的是原foo函數(shù)對(duì)象

例2:被裝飾的函數(shù)有參數(shù)

from time import ctime, sleep

def timefun(func):
    def wrappedfunc(a, b):
        print("%s called at %s"%(func.__name__, ctime()))
        print(a, b)
        func(a, b)
    return wrappedfunc

@timefun
def foo(a, b):
    print(a+b)

foo(3,5)
sleep(2)
foo(2,4)

例3:被裝飾的函數(shù)有不定長(zhǎng)參數(shù)

from time import ctime, sleep

def timefun(func):
    def wrappedfunc(*args, **kwargs):
        print("%s called at %s"%(func.__name__, ctime()))
        func(*args, **kwargs)
    return wrappedfunc

@timefun
def foo(a, b, c):
    print(a+b+c)

foo(3,5,7)
sleep(2)
foo(2,4,9)

例4:裝飾器中的return

from time import ctime, sleep

def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        func()
    return wrappedfunc

@timefun
def foo():
    print("I am foo")

@timefun
def getInfo():
    return '----hahah---'

foo()
sleep(2)
foo()


print(getInfo())

執(zhí)行結(jié)果:

foo called at Fri Nov  4 21:55:35 2016
I am foo
foo called at Fri Nov  4 21:55:37 2016
I am foo
getInfo called at Fri Nov  4 21:55:37 2016
None

如果修改裝飾器為return func(),則運(yùn)行結(jié)果:

foo called at Fri Nov  4 21:55:57 2016
I am foo
foo called at Fri Nov  4 21:55:59 2016
I am foo
getInfo called at Fri Nov  4 21:55:59 2016
----hahah---
總結(jié):
  • 一般情況下為了讓裝飾器更通用砚殿,可以有return

例5:裝飾器帶參數(shù),在原有裝飾器的基礎(chǔ)上啃憎,設(shè)置外部變量

#decorator2.py

from time import ctime, sleep

def timefun_arg(pre="hello"):
    def timefun(func):
        def wrappedfunc():
            print("%s called at %s %s"%(func.__name__, ctime(), pre))
            return func()
        return wrappedfunc
    return timefun

@timefun_arg("itcast")
def foo():
    print("I am foo")

@timefun_arg("python")
def too():
    print("I am too")

foo()
sleep(2)
foo()

too()
sleep(2)
too()

可以理解為

foo()==timefun_arg("itcast")(foo)()

例6:類裝飾器(擴(kuò)展,非重點(diǎn))

裝飾器函數(shù)其實(shí)是這樣一個(gè)接口約束似炎,它必須接受一個(gè)callable對(duì)象作為參數(shù)辛萍,然后返回一個(gè)callable對(duì)象。在Python中一般callable對(duì)象都是函數(shù)羡藐,但也有例外贩毕。只要某個(gè)對(duì)象重寫了 __call__()方法,那么這個(gè)對(duì)象就是callable的仆嗦。

class Test():
    def __call__(self):
        print('call me!')

t = Test()
t()  # call me

類裝飾器demo

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---

下一篇文章為:→1.2.1import導(dǎo)入模塊
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市栽燕,隨后出現(xiàn)的幾起案子罕袋,更是在濱河造成了極大的恐慌改淑,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浴讯,死亡現(xiàn)場(chǎng)離奇詭異朵夏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)榆纽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侍郭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人掠河,你說(shuō)我怎么就攤上這事∶图疲” “怎么了唠摹?”我有些...
    開(kāi)封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奉瘤。 經(jīng)常有香客問(wèn)我勾拉,道長(zhǎng),這世上最難降的妖魔是什么盗温? 我笑而不...
    開(kāi)封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任藕赞,我火速辦了婚禮,結(jié)果婚禮上卖局,老公的妹妹穿的比我還像新娘斧蜕。我一直安慰自己,他們只是感情好砚偶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布批销。 她就那樣靜靜地躺著,像睡著了一般染坯。 火紅的嫁衣襯著肌膚如雪均芽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天单鹿,我揣著相機(jī)與錄音掀宋,去河邊找鬼。 笑死仲锄,一個(gè)胖子當(dāng)著我的面吹牛劲妙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昼窗,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼是趴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了澄惊?” 一聲冷哼從身側(cè)響起唆途,我...
    開(kāi)封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤富雅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后肛搬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體没佑,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年温赔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛤奢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陶贼,死狀恐怖啤贩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拜秧,我是刑警寧澤痹屹,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站枉氮,受9級(jí)特大地震影響志衍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聊替,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一楼肪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惹悄,春花似錦春叫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至爷速,卻和暖如春央星,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惫东。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工莉给, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廉沮。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓颓遏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親滞时。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叁幢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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

  • 1.1==,is的使用 ·is是比較兩個(gè)引用是否指向了同一個(gè)對(duì)象(引用比較)坪稽。 ·==是比較兩個(gè)對(duì)象是否相等曼玩。 1...
    TENG書閱讀 731評(píng)論 0 0
  • 1.1裝飾器 裝飾器是程序開(kāi)發(fā)中經(jīng)常會(huì)用到的一個(gè)功能鳞骤,用好了裝飾器,開(kāi)發(fā)效率如虎添翼黍判,所以這也是Python面試中...
    PythonMaO閱讀 482評(píng)論 1 5
  • 很多寫裝飾器的都是直接甩給你最終的裝飾器代碼,然后給你說(shuō)下大致的原理,比如: 然后來(lái)上一句,把@log放到now(...
    ztfdeveloper閱讀 312評(píng)論 0 0
  • 運(yùn)行結(jié)果如下: 閉包的定義:在函數(shù)內(nèi)部再定義一個(gè)函數(shù)豫尽,并且這個(gè)函數(shù)用到了外邊函數(shù)的變量,那么將這個(gè)函數(shù)以及用到的一...
    魔法高校的劣等生閱讀 518評(píng)論 0 0
  • 【作者按】本文所涉大多是舊書顷帖。近年我住在美國(guó)中部小城美旧,為潮流所棄。信息僻陋贬墩,令唾沫無(wú)緣沾污炙手的杰作榴嗅;知識(shí)稀松,催...
    草茅閱讀 640評(píng)論 5 7