Python裝飾器

在Python里面,函數(shù)可以作為參數(shù)傳入一個函數(shù)雀摘,函數(shù)也可以復(fù)制給變量勋又,通過變量調(diào)用函數(shù)苦掘。裝飾器可以擴展一個函數(shù)的功能,為函數(shù)做一個裝飾器注解楔壤,可以把裝飾器里面定義的功能于所有函數(shù)提前執(zhí)行鹤啡,提升代碼的復(fù)用程度。

現(xiàn)在有這么個場景蹲嚣。

打卡

互聯(lián)網(wǎng)公司里面有各種員工递瑰,程序員,前臺...端铛,程序員在打開電腦前泣矛,需要打卡,前臺要早點來開門(我也不清楚禾蚕,誰開門您朽,這里假定,前臺開門),前臺開門前也需要打卡哗总。也就是說几颜,打卡是所有員工的最先的公共動作,那么可以把打卡這個功能抽出來作為公共邏輯讯屈。

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

自然想到蛋哭,可以實現(xiàn)如下。

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


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


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


if __name__ == '__main__':
    """
    程序員開機之前涮母,前臺開門之前谆趾,都需要先在門外指紋機打卡。
    """
    di(boot)
    di(open)

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

注意:__name__如果作為模塊導(dǎo)入来候,module.__name__就是模塊自己的名字跷叉,如果模塊自己作為腳本執(zhí)行,返回__main__营搅。

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

boot 打卡,滴...
開機
open 打卡,滴...
開門

這樣設(shè)計云挟,如果有很多函數(shù)都要調(diào)用,就很麻煩转质,那么裝飾器就排上了用場园欣。

簡單裝飾器 與 @語法糖

裝飾器:在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)休蟹。

簡單裝飾器

定義一個di(f)方法俊庇,還是把要執(zhí)行的邏輯的函數(shù)作為參數(shù)傳入,里面定義一個wrapper函數(shù)鸡挠,返回值是f的執(zhí)行結(jié)果。
if __name__ == '__main__':里面搬男,調(diào)用了這個裝飾器拣展,不修改定義好了的函數(shù),在運行期間動態(tài)添加功能"打卡"缔逛。

import functools

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


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


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 打卡,滴...
開機
open 打卡,滴...
開門

由于di(boot)的返回值a就是wrapper函數(shù)堪遂,那么print(a.__name__)的結(jié)果就理所當(dāng)然是是wrapper介蛉,我們希望是boot,怎么辦溶褪,functools.wraps(f)這個注解可以把原始函數(shù)boot__name__等屬性復(fù)制到wrapper()币旧,把這行代碼注釋也能運行,那么print(a.__name__)的結(jié)果就是wrapper猿妈。

第二種吹菱,@ 語法糖

通過@語法糖,也能將裝飾器應(yīng)用于函數(shù)上面彭则,推薦鳍刷。

import functools

def di(f):
    """
    程序員開機之前,前臺開門之前贰剥,都需要先在門外指紋機打卡倾剿。
    :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('開機')


@di
def open2():
    print('開門')
    
    
if __name__ == '__main__':

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

@di標記相當(dāng)于蚌成,a2 = di(boot2) a2()前痘。不用這么麻煩,因為加了@符號標記担忧,直接用boot2()調(diào)用裝飾器即可芹缔。

結(jié)果

boot2 打卡,滴...
開機
open2 打卡,滴...
開門

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

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

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

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

import functools

# 業(yè)務(wù)邏輯函數(shù)需要參數(shù)
def di(f):
    """
    程序員開機之前最欠,前臺開門之前,都需要先在門外指紋機打卡惩猫。
    :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 開機' % name)


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

結(jié)果:

boot 打卡,滴...
keguang 開機

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奶镶,咱可以做個表揚迟赃,那么相當(dāng)于只需要在前面的di()外面套一層函數(shù),di_args即可,在wrapper里面厂镇。使用這個參數(shù)

import functools

# 帶參數(shù)的裝飾器
def di_args(time):
    def di(f):
        """
        程序員開機之前纤壁,前臺開門之前,都需要先在門外指紋機打卡捺信。
        :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 開機' % name)


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

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

來的真早茵臭,很棒。舅世。旦委。
boot 打卡,滴...
keguang 開機

類裝飾器

類裝飾器主要依靠類的__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('開機')


if __name__ == '__main__':
    boot()

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

decorator start...
開機
decorator end...

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萍倡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辟汰,更是在濱河造成了極大的恐慌列敲,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帖汞,死亡現(xiàn)場離奇詭異戴而,居然都是意外死亡,警方通過查閱死者的電腦和手機翩蘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門填硕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鹿鳖,你說我怎么就攤上這事∽秤ǎ” “怎么了翅帜?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長命满。 經(jīng)常有香客問我涝滴,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任歼疮,我火速辦了婚禮杂抽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘韩脏。我一直安慰自己缩麸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布赡矢。 她就那樣靜靜地躺著杭朱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吹散。 梳的紋絲不亂的頭發(fā)上弧械,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音空民,去河邊找鬼刃唐。 笑死,一個胖子當(dāng)著我的面吹牛界轩,可吹牛的內(nèi)容都是我干的画饥。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼耸棒,長吁一口氣:“原來是場噩夢啊……” “哼荒澡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起与殃,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤单山,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幅疼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體米奸,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年爽篷,在試婚紗的時候發(fā)現(xiàn)自己被綠了悴晰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡逐工,死狀恐怖铡溪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泪喊,我是刑警寧澤棕硫,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站袒啼,受9級特大地震影響哈扮,放射性物質(zhì)發(fā)生泄漏纬纪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一滑肉、第九天 我趴在偏房一處隱蔽的房頂上張望包各。 院中可真熱鬧,春花似錦靶庙、人聲如沸问畅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽按声。三九已至,卻和暖如春恬吕,著一層夾襖步出監(jiān)牢的瞬間签则,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工铐料, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留渐裂,地道東北人。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓钠惩,卻偏偏與公主長得像柒凉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子篓跛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

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

  • 一 裝飾器介紹 1.1 為何要用裝飾器 軟件的設(shè)計應(yīng)該遵循開放封閉原則愧沟,即對擴展是開放的蔬咬,而對修改是封閉的。對擴展...
    100斤的瘦子_湯勇閱讀 566評論 0 0
  • 一沐寺、裝飾器的基本使用 在不改變函數(shù)源代碼的前提下林艘,給函數(shù)添加新的功能,這時就需要用到“裝飾器”混坞。 0.開放封閉原則...
    NJingZYuan閱讀 524評論 0 0
  • 部分細節(jié)自己改了點狐援,也加了點自己例子,基本上屬于轉(zhuǎn)載究孕。轉(zhuǎn)載出處:https://my.oschina.net/le...
    洛克黃瓜閱讀 1,970評論 0 3
  • Python中的裝飾器是你進入Python大門的一道坎啥酱,不管你跨不跨過去它都在那里。 為什么需要裝飾器 我們假設(shè)你...
    忘了呼吸的那只貓閱讀 516評論 0 1
  • Python中的裝飾器是你進入Python大門的一道坎厨诸,不管你跨不跨過去它都在那里懈涛。 為什么需要裝飾器 我們假設(shè)你...
    TestingShare閱讀 721評論 0 7