python 裝飾器的使用詳解

注意事項

  • 何時執(zhí)行裝飾器
    函數(shù)裝飾器在導(dǎo)入模塊時立即執(zhí)行,而被裝飾的函數(shù)只在明確調(diào)用時運行霉囚。這突出了 Python 程序員所說的導(dǎo)入時和運 行時之間的區(qū)別捕仔。
    Python何時執(zhí)行裝飾器 - 頑強的allin - 博客園
  • 裝飾器是可以疊加使用的,那么使用裝飾器以后代碼是什么順序呢
    對于Python中的@語法糖,裝飾器的調(diào)用順序與使用 @語法糖聲明的順序相反榜跌。
    比如
@decorator1
@decorator2
def f(a,b):
    pass

執(zhí)行順序是
f(3, 4) = decorator1(decorator2(f(3, 4)))

  • @decorator這個語法相當(dāng)于 執(zhí)行func = decorator(func)闪唆,為func函數(shù)裝飾并返回

裝飾函數(shù)

(1)被裝飾器的函數(shù)帶有形參

# -*- coding:utf-8 -*-
from enum import Enum, unique
import time

def print_run_time(fun):
    def wrapper(*args, **kwargs):
        print('args:', args)
        print('kwargs:', kwargs)
        start_time = time.time()
        fun(*args, **kwargs)
        run_time = time.time() - start_time
        print('run_time is:', run_time)
    return wrapper

@print_run_time
def test_fun(min_value, max_value):
    sum = 0
    for i in range(min_value, max_value, 1):
        sum += i
    print('sum is:', sum)

wrapper(*args, **kwargs)用來處理被裝飾的函數(shù)帶有各種形參的情況。

  • 當(dāng)調(diào)用
if __name__ == '__main__':
    test_fun(1, 1000000)

輸出

('args:', (1, 1000000))
('kwargs:', {})
('sum is:', 499999500000L)
('run_time is:', 0.07899999618530273)

當(dāng)調(diào)用

if __name__ == '__main__':
    test_fun(min_value=1, max_value=1000000)

輸出

('args:', ())
('kwargs:', {'max_value': 1000000, 'min_value': 1})
('sum is:', 499999500000L)
('run_time is:', 0.08299994468688965)

(2)被裝飾器的函數(shù)帶有返回值

def print_run_time(fun):
    def wrapper(*args, **kwargs):
        print('args:', args)
        print('kwargs:', kwargs)
        start_time = time.time()
        result = fun(*args, **kwargs)
        run_time = time.time() - start_time
        print('run_time is:', run_time)
        return result
    return wrapper

@print_run_time
def test_fun(min_value, max_value):
    sum = 0
    for i in range(min_value, max_value, 1):
        sum += i
    print('sum is:', sum)
    return sum

if __name__ == '__main__':
    result = test_fun(min_value=1, max_value=1000000)
    print('result:', result)

(3)裝飾器帶有形參
帶有參數(shù)的裝飾器需要再增加一層封裝

def print_run_time(debug_flag):
    def fun_wrapper(fun):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = fun(*args, **kwargs)
            run_time = time.time() - start_time
            if debug_flag:
                print('args:', args)
                print('kwargs:', kwargs)
                print('run_time is:', run_time)
            return result
        return wrapper
    return fun_wrapper

DEBUG_FLAG = True
@print_run_time(DEBUG_FLAG)
def test_fun(min_value, max_value):
    sum = 0
    for i in range(min_value, max_value, 1):
        sum += i
    print('sum is:', sum)
    return sum

例子中想要給裝飾器添加參數(shù)钓葫,需要再增加一層封裝悄蕾。比如這里做一個輸出開關(guān)。當(dāng)DEBUG_FLAG = True才輸出信息础浮。

(4)@wraps(func)的作用
Python裝飾器(decorator)在實現(xiàn)的時候帆调,被裝飾后的函數(shù)其實已經(jīng)是另外一個函數(shù)了(函數(shù)名等函數(shù)屬性會發(fā)生改變),為了不影響豆同,Pythonfunctools包中提供了一個叫wrapsdecorator來消除這樣的副作用番刊。寫一個decorator的時候,最好在實現(xiàn)之前加上functoolswrap诱告,它能保留原有函數(shù)的名稱撵枢。

  • 不加wrap
if __name__ == '__main__':
    result = test_fun(min_value=1, max_value=1000000)
    print('fun_name', test_fun.__name__)

輸出

('fun_name', 'wrapper')
  • 添加wrap
from functools import wraps

def print_run_time(debug_flag):
    def fun_wrapper(fun):
        @wraps(fun)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = fun(*args, **kwargs)
            run_time = time.time() - start_time
            if debug_flag:
                print('args:', args)
                print('kwargs:', kwargs)
                print('run_time is:', run_time)
            return result
        return wrapper
    return fun_wrapper

輸出結(jié)果:

('fun_name', 'test_fun')

裝飾類民晒,或者裝飾類中的方法

# -*- coding:utf-8 -*-
from enum import Enum, unique
import time
from functools import wraps
SKILL_MAP = {}
DEBUG_FLAG = True

def register_skill(skill_type):
    def wrapper(cls):
        global SKILL_MAP
        assert cls.skill_cd > 0, 'skill_cd: %s should > 0' % (cls.skill_cd,)
        SKILL_MAP[skill_type] = cls
        return cls
    return wrapper


def print_run_time(debug_flag):
    def fun_wrapper(fun):
        @wraps(fun)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = fun(*args, **kwargs)
            run_time = time.time() - start_time
            if debug_flag:
                print('args:', args)
                print('kwargs:', kwargs)
                print('run_time is:', run_time)
            return result
        return wrapper
    return fun_wrapper

@unique
class SkillType(Enum):
    BUFF = 1
    FLY = 2


class BaseSkill(object):
    skill_cd = 0

    @print_run_time(DEBUG_FLAG)
    def create_skill(self):
        pass

    def destroy_skill(self):
        pass


@register_skill(SkillType.BUFF)
class BuffSkill(BaseSkill):
    skill_cd = 1.0

    def create_skill(self):
        super(BuffSkill, self).create_skill()
        print('create buff skill')

    def destroy_skill(self):
        print('destroy buff skill')


@register_skill(SkillType.FLY)
class FlySkill(BaseSkill):
    skill_cd = 2.0

    def create_skill(self):
        super(FlySkill, self).create_skill()
        print('create fly skill')

    def destroy_skill(self):
        super(FlySkill, self).destroy_skill()
        print('destroy fly skill')

def skill_facory(skill_type):
    skill_class = SKILL_MAP[skill_type]
    skill = skill_class()
    return skill

if __name__ == '__main__':
    skill = skill_facory(SkillType.FLY)
    skill.create_skill()
    skill.destroy_skill()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末精居,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子潜必,更是在濱河造成了極大的恐慌靴姿,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佛吓,死亡現(xiàn)場離奇詭異垂攘,居然都是意外死亡,警方通過查閱死者的電腦和手機吱型,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門陨仅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人触徐,你說我怎么就攤上這事狐赡。” “怎么了孔祸?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長崔慧。 經(jīng)常有香客問我,道長温自,這世上最難降的妖魔是什么皇钞? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任夹界,我火速辦了婚禮,結(jié)果婚禮上可柿,老公的妹妹穿的比我還像新娘。我一直安慰自己营密,他們只是感情好目锭,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著被去,像睡著了一般奖唯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上踪央,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天瓢阴,我揣著相機與錄音,去河邊找鬼液斜。 笑死,一個胖子當(dāng)著我的面吹牛少漆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播示损,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼检访,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了医清?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤会烙,失蹤者是張志新(化名)和其女友劉穎柏腻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葫盼,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡村斟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年蟆盹,在試婚紗的時候發(fā)現(xiàn)自己被綠了闺金。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡寨昙,死狀恐怖掀亩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捉蚤,我是刑警寧澤抬驴,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布布持,位于F島的核電站陕悬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芙委。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一灌侣、第九天 我趴在偏房一處隱蔽的房頂上張望侧啼。 院中可真熱鬧,春花似錦痊乾、人聲如沸椭更。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叽奥,卻和暖如春痛侍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背主届。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工君丁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谈截。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓涧偷,卻偏偏與公主長得像燎潮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子确封,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348