美文网首页
python 装饰器的使用详解

python 装饰器的使用详解

作者: 远行_2a22 | 来源:发表于2019-11-26 15:47 被阅读0次

注意事项

  • 何时执行装饰器
    函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了 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()

相关文章

网友评论

      本文标题:python 装饰器的使用详解

      本文链接:https://www.haomeiwen.com/subject/fsxaictx.html