了解女朋友的心還不如去了解Python之Python裝飾器

在Python里面,函數(shù)可以作為參數(shù)傳入一個函數(shù)氯迂,函數(shù)也可以復(fù)制給變量践叠,通過變量調(diào)用函數(shù)言缤。裝飾器可以擴(kuò)展一個函數(shù)的功能,為函數(shù)做一個裝飾器注解禁灼,可以把裝飾器里面定義的功能于所有函數(shù)提前執(zhí)行管挟,提升代碼的復(fù)用程度。

那么現(xiàn)在有這樣一個場景弄捕。

打卡

互聯(lián)網(wǎng)公司里面有各種各樣的職位僻孝,比如程序員、前臺...守谓,程序員在打開電腦前穿铆,需要打卡,前臺要早點(diǎn)來開門(其實(shí)我也不清楚一般是誰開門斋荞,這里先假定為前臺開門)荞雏,前臺開門前也需要打卡。也就是說平酿,打卡是所有員工的最先的公共動作凤优,那么可以把打卡這個功能抽出來作為公共邏輯。

普通函數(shù)調(diào)用方法

自然想到蜈彼,可以實(shí)現(xiàn)如下筑辨。

def di(f):
    print('%s 打卡,滴...' % f.__name__)
    return f()

def boot():
    print('開機(jī)')

def open():
    print('開門')

if __name__ == '__main__':
    """
    程序員開機(jī)之前,前臺開門之前幸逆,都需要先在門外指紋機(jī)打卡棍辕。
    """
    di(boot)
    di(open)

定義了一個函數(shù)di(f)暮现,可以打印f.__name__f的函數(shù)名信息,同時返回f()的執(zhí)行結(jié)果楚昭。

注意:__name__如果作為模塊導(dǎo)入送矩,module.__name__就是模塊自己的名字,如果模塊自己作為腳本執(zhí)行哪替,返回__main__栋荸。

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

boot 打卡,滴...
開機(jī)
open 打卡,滴...
開門

這樣設(shè)計,如果有很多函數(shù)都要調(diào)用凭舶,就很麻煩晌块,那么裝飾器就排上了用場。

簡單裝飾器 與 @語法糖

裝飾器:在代碼運(yùn)行期間動態(tài)增加功能的方式帅霜,稱之為“裝飾器”(Decorator)匆背。

簡單裝飾器

定義一個di(f)方法,還是把要執(zhí)行的邏輯的函數(shù)作為參數(shù)傳入身冀,里面定義一個wrapper函數(shù)钝尸,返回值是f的執(zhí)行結(jié)果。 在if __name__ == '__main__':里面搂根,調(diào)用了這個裝飾器珍促,不修改定義好了的函數(shù),在運(yùn)行期間動態(tài)添加功能"打卡"剩愧。

import functools

# 簡單裝飾器
def di(f):
    """
    程序員開機(jī)之前猪叙,前臺開門之前,都需要先在門外指紋機(jī)打卡仁卷。
    :param f: 傳入一個函數(shù)
    :return:
    """
    # 把原始函數(shù)的__name__等屬性復(fù)制到wrapper()
    @functools.wraps(f)
    def wrapper():
        print('%s 打卡,滴...' % f.__name__)
        return f()
    return wrapper

def boot():
    print('開機(jī)')

def open():
    print('開門')

if __name__ == '__main__':

    # 第一種穴翩,簡單裝飾器
    a = di(boot)
    a1 = di(open)
    print(a.__name__) # 結(jié)果wrapper 加@functools.wraps(f)后結(jié)果為 boot
    a()
    a1()

di(boot)的返回值a就是wrapper函數(shù),通過a()就調(diào)用了wrapper函數(shù)锦积,得到boot的返回值芒帕。同理,di(open)一樣丰介。

結(jié)果

boot
boot 打卡,滴...
開機(jī)
open 打卡,滴...
開門

由于di(boot)的返回值a就是wrapper函數(shù)背蟆,那么print(a.__name__)的結(jié)果就理所當(dāng)然是是wrapper,我們希望是boot基矮,怎么辦淆储,functools.wraps(f)這個注解可以把原始函數(shù)boot__name__等屬性復(fù)制到wrapper(),把這行代碼注釋也能運(yùn)行家浇,那么print(a.__name__)的結(jié)果就是wrapper本砰。

第二種,@ 語法糖

通過@語法糖钢悲,也能將裝飾器應(yīng)用于函數(shù)上面点额,推薦舔株。

import functools

def di(f):
    """
    程序員開機(jī)之前,前臺開門之前还棱,都需要先在門外指紋機(jī)打卡载慈。
    :param f: 傳入一個函數(shù)
    :return:
    """
    # 把原始函數(shù)的__name__等屬性復(fù)制到wrapper()
    @functools.wraps(f)
    def wrapper():
        print('%s 打卡,滴...' % f.__name__)
        return f()
    return wrapper

# @ 語法糖
@di
def boot2():
    print('開機(jī)')

@di
def open2():
    print('開門')

if __name__ == '__main__':

    # 第二種,@ 語法糖
    boot2()
    open2()

@di標(biāo)記相當(dāng)于珍手,a2 = di(boot2) a2()办铡。不用這么麻煩,因為加了@符號標(biāo)記琳要,直接用boot2()調(diào)用裝飾器即可寡具。

結(jié)果

boot2 打卡,滴...
開機(jī)
open2 打卡,滴...
開門

業(yè)務(wù)邏輯函數(shù)需要參數(shù)

業(yè)務(wù)邏輯函數(shù)可能需要參數(shù),比如:

def boot(name):
    print('%s 開機(jī)' % name)

那么稚补,只需要將前面的裝飾器修改為:

import functools

# 業(yè)務(wù)邏輯函數(shù)需要參數(shù)
def di(f):
    """
    程序員開機(jī)之前童叠,前臺開門之前,都需要先在門外指紋機(jī)打卡课幕。
    :param f: 傳入一個函數(shù)
    :return:
    """
    # 把原始函數(shù)的__name__等屬性復(fù)制到wrapper()
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print('%s 打卡,滴...' % f.__name__)
        return f(*args, **kwargs)
    return wrapper

@di
def boot(name):
    print('%s 開機(jī)' % name)

if __name__ == '__main__':
    boot('keguang')

結(jié)果:

boot 打卡,滴...
keguang 開機(jī)

wrapper也加上*args, **kwargs參數(shù)厦坛,在boot里面直接調(diào)用f(*args, **kwargs)即可。順便提一下:

  • *args:可以傳入一個數(shù)組參數(shù)
  • **kwargs:可以傳入一個k-v對參數(shù)

先后順序?qū)?yīng)乍惊,數(shù)組參數(shù)在前杜秸。舉例:

def f(*args, **kwargs):
    print('args=', args)
    print('kwargs=', kwargs)

print(f(1, 2, 3, a = 'a', b = 'b'))

# 結(jié)果
# args= (1, 2, 3)
# kwargs= {'a': 'a', 'b': 'b'}

帶參數(shù)的裝飾器

如果裝飾器也帶參數(shù),比如現(xiàn)在如果某個員工早晨上班來得早< 9:00污桦,咱可以做個表揚(yáng)亩歹,那么相當(dāng)于只需要在前面的di()外面套一層函數(shù),di_args即可,在wrapper里面凡橱。使用這個參數(shù)

import functools

# 帶參數(shù)的裝飾器
def di_args(time):
    def di(f):
        """
        程序員開機(jī)之前,前臺開門之前亭姥,都需要先在門外指紋機(jī)打卡稼钩。
        :param f: 傳入一個函數(shù)
        :return:
        """
        # 把原始函數(shù)的__name__等屬性復(fù)制到wrapper()
        @functools.wraps(f)
        def wrapper(*args, **kwargs):
            if time < '9:00':
                print('來的真早,很棒达罗。坝撑。。')

            print('%s 打卡,滴...' % f.__name__)
            return f(*args, **kwargs)
        return wrapper
    return di

@di_args('8:00')
def boot(name):
    print('%s 開機(jī)' % name)

if __name__ == '__main__':
    boot('keguang')

參數(shù)在@di_args('8:00')傳入即可粮揉,有點(diǎn)像java里面的注解巡李。最后還是通過boot('keguang')調(diào)用即可,結(jié)果:

來的真早扶认,很棒多糠。赴邻。。
boot 打卡,滴...
keguang 開機(jī)

類裝飾器

類裝飾器主要依靠類的__call__方法翅睛,當(dāng)使用 @ 形式將裝飾器附加到函數(shù)上時,就會調(diào)用此方法海洼。

# 類裝飾器
class di(object):
    def __init__(self, f):
        self._f = f

    def __call__(self, *args, **kwargs):
        print('decorator start...')
        self._f()
        print('decorator end...')

@di
def boot():
    print('開機(jī)')

if __name__ == '__main__':
    boot()

加上@di裝飾器標(biāo)識,會用boot去實(shí)例化di類,然后執(zhí)行__call__函數(shù)敞葛,object表示這個類可以傳入任何類型參數(shù)。 運(yùn)行結(jié)果

decorator start...
開機(jī)
decorator end...

裝飾器有一個典型的應(yīng)用場景就是打log日志与涡,如果所有邏輯都需要日志記錄程序的運(yùn)行狀況惹谐,那么可以對這些邏輯(函數(shù))加日志模塊裝飾器,就能達(dá)到相應(yīng)目的驼卖。

最后

如果文章對你有用的話豺鼻,給作者點(diǎn)點(diǎn)贊吧
拒絕白嫖,從我做起

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末款慨,一起剝皮案震驚了整個濱河市儒飒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌檩奠,老刑警劉巖桩了,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異埠戳,居然都是意外死亡井誉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門整胃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颗圣,“玉大人,你說我怎么就攤上這事屁使≡谄瘢” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵蛮寂,是天一觀的道長蔽午。 經(jīng)常有香客問我,道長酬蹋,這世上最難降的妖魔是什么及老? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮范抓,結(jié)果婚禮上骄恶,老公的妹妹穿的比我還像新娘。我一直安慰自己匕垫,他們只是感情好僧鲁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般悔捶。 火紅的嫁衣襯著肌膚如雪铃慷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天蜕该,我揣著相機(jī)與錄音犁柜,去河邊找鬼。 笑死堂淡,一個胖子當(dāng)著我的面吹牛馋缅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绢淀,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼萤悴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了皆的?” 一聲冷哼從身側(cè)響起覆履,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎费薄,沒想到半個月后硝全,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楞抡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年伟众,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片召廷。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡凳厢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竞慢,到底是詐尸還是另有隱情先紫,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布梗顺,位于F島的核電站泡孩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寺谤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一吮播、第九天 我趴在偏房一處隱蔽的房頂上張望变屁。 院中可真熱鬧,春花似錦意狠、人聲如沸粟关。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闷板。三九已至澎灸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遮晚,已是汗流浹背性昭。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留县遣,地道東北人糜颠。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像萧求,于是被迫代替她去往敵國和親其兴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

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