引言:Python中的装饰器 是指在不修改被装饰对象的原代码以及调用方式的前提下为被装饰对象添加新功能的可调用对象,有学习过Java的童鞋可能知道AOP(Aspect Oriented Programming)编程,它们有一定的相似性可以拿来一起借鉴对比学习。

一、装饰器基本概念

       装饰器(Decorator)是一种特殊的函数,它可以修改或增强其他函数的功能。装饰器的本质是一个高阶函数,它接收一个函数作为参数,返回一个新的函数作为结果。常见的装饰器一般这个类别:

  • 修饰函数无参数无返回值的装饰器
  • 修饰函数有参数有返回值的装饰器
  • 装饰器传递参数(高级装饰器)
  • 类装饰器
  • 多装饰器嵌套
二、装饰器定义格式
  • 修饰函数无参数无返回值的装饰器
def log(level):
    def decorator(func):
        def wrapper():
            print(f"执行了无参数和无返回值的装饰器,函数名: {func.__name__}")
            func()
        return wrapper
    return decorator
    
# 目标函数
@log()
def target():
    print('执行了目标')
    
# 主函数入口
def main():
    target()

if __name__ == '__main__':
    main()
  • 修饰函数有参数有返回值的装饰器
def log(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"执行了有参数和有返回值的装饰器,函数名: {func.__name__} args:{args} kwargs:{kwargs}")
            return func(*args, **kwargs)
        return wrapper
    return decorator
    
# 目标函数
@log()
def target(param1, param2):
    print(f'执行了目标函数 参数1:{param1} 参数2:{param2}')
    return param1 + param2
    
# 主函数入口
def main():
    result = target(1, 2)
    print(f'返回结果:{result}')

if __name__ == '__main__':
    main()
  • 装饰器传递参数(高级装饰器)
# 定义日志装饰器
def log(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"执行了有参数有返回值且带参数的装饰器,装饰器参数:{level} 函数名:{func.__name__} args:{args} kwargs:{kwargs}")
            if 'INFO' == level:
                print(f'执行日志级别为:{level}')
            elif 'WARN' == level:
                print(f'执行日志级别为:{level}')
            else:
                print(f'执行日志级别为:{level}')
            return func(*args, **kwargs)
        return wrapper
    return decorator
    
# 目标函数(指定参数名)
@log(level="INFO")
def targetAdd(param1, param2):
    print(f'执行了目标函数 参数一:{param1} 参数二:{param2}')
    return param1 + param2

# 目标函数(指定参数名)
@log(level="WARN")
def targetSub(param1, param2):
    print(f'执行了目标函数 参数一:{param1} 参数二:{param2}')
    return param1 - param2
    
# 主函数入口
def main():
    result1 = targetAdd(2, 2)
    print(f'返回结果:{result1}')

    result2 = targetSub(3, 1)
    print(f'返回结果:{result2}')

if __name__ == '__main__':
    main()
  • 类装饰器
# 类装饰器
class CheckLogin(object):
    # 定一个初始化方法用户接收fn函数的内存地址
    def __init__(self, fn):
        # 把函数的内存地址赋值给类的私有属性
        self.__fn = fn

    # 定一个__call__魔法值方法,用于把类当作一个函数调用
    def __call__(self, *args, **kwargs):
        # 装饰器的功能逻辑
        print('请先登录')
        
        # 调用装饰器修饰的函数
        self.__fn()

@CheckLogin
def comment():
    print('开始编写评论')

# 主函数入口
def main():
    comment()

if __name__ == '__main__':
    main()
  • 多装饰器嵌套
import time

# 函数计时器装饰器
def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数执行耗时: {end_time - start_time:.2f} s")
        return result
    return wrapper

# 函数执行结果缓存装饰器
def cache(func):
    cache_dict = {}
    def wrapper(*args, **kwargs):
        key = str(args) + str(kwargs)
        # 在缓存中查找函数执行结果,若存在则直接返回
        if key in cache_dict:
            print("根据key查找缓存数据")
            return cache_dict[key]
        else:
            result = func(*args, **kwargs)
            cache_dict[key] = result
            return result
    return wrapper    
    
@cache
@timer
def target(param1, param2):
    time.sleep(1)
    return param1 + param2
    
# 主函数入口
def main():
    comment()
    result = target(2, 2)
    print(f'返回结果:{result}')

if __name__ == '__main__':
    main()
三、装饰器优点
  • 代码重用:无需重复代码多个函数可以共用同一个装饰器。
  • 封装:装饰器可以在不修改原函数的情况下增加额外功能。
  • 层次清晰:装饰器可以清晰地表明添加的额外功能。
  • 可组合:多个装饰器可以组合使用,以增强函数的功能。
四、装饰器应用场景
  • 日志记录:记录函数的调用时间、参数和返回值等信息;
  • 计时器:统计函数的执行时间;
  • 缓存:缓存函数的计算结果,避免重复计算;
  • 权限/身份验证:验证用户是否有权限执行某个函数;
  • 错误处理:捕获函数执行过程中的异常,并进行处理等。
  • 限制函数调用次数:给函数设定调用次数,单个进程或线程内或时间节点内不允许超出调用限制
  • 重试:函数执行不符合预期时,进行重新调用执行
五、总结

       Python装饰器是一种强大且灵活的机制,允许我们灵活的增强函数的功能和修改函数的行为。学会使用装饰器可以使你的代码更加健壮,具有更好的可重用性。同时高阶装饰器可以让你更灵活的处理多种需求。在实际开发中,我们可以根据具体的需求来选择合适的装饰器。

[1]如何用Python装饰器让你的代码更加优雅,一文带你理解Python装饰器![EB/OL].