python 装饰器的使用详解

注意事项

  • 何时执行装饰器
    函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了 Python 程序员所说的导入时和运 行时之间的区别。
    Python何时执行装饰器 - 顽强的allin - 博客园
  • 装饰器是可以叠加使用的,那么使用装饰器以后代码是什么顺序呢
    对于Python中的@语法糖,装饰器的调用顺序与使用 @语法糖声明的顺序相反。
    比如
@decorator1
@decorator2
def f(a,b):
    pass

执行顺序是
f(3, 4) = decorator1(decorator2(f(3, 4)))

  • @decorator这个语法相当于 执行func = decorator(func),为func函数装饰并返回

装饰函数

(1)被装饰器的函数带有形参

# -*- 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)用来处理被装饰的函数带有各种形参的情况。

  • 当调用
if __name__ == '__main__':
    test_fun(1, 1000000)

输出

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

当调用

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)被装饰器的函数带有返回值

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)装饰器带有形参
带有参数的装饰器需要再增加一层封装

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

例子中想要给装饰器添加参数,需要再增加一层封装。比如这里做一个输出开关。当DEBUG_FLAG = True才输出信息。

(4)@wraps(func)的作用
Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Pythonfunctools包中提供了一个叫wrapsdecorator来消除这样的副作用。写一个decorator的时候,最好在实现之前加上functoolswrap,它能保留原有函数的名称。

  • 不加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

输出结果:

('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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,640评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,254评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,011评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,755评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,774评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,610评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,352评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,257评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,717评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,894评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,021评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,735评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,354评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,936评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,054评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,224评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,974评论 2 355