文章目录

  • 1 装饰器介绍
  • 2 装饰器的主要作用
  • 3 装饰器的常见应用场景
  • 4 装饰器基本语法
  • 4.1 传统方式
  • 4.2 语法糖
  • 5 使用案例
  • 5.1 无参无返回
  • 5.2 有参无返回
  • 5.3无参有返回
  • 5.4 有参有返回
  • 5.5 可变参数
  • 5.6 多个修饰器的使用
  • 5.7 带参数的装饰器
  • 5.8 类装饰器

  • 1 装饰器介绍

    Python 装饰器(Decorator)是一种用于修改或扩展函数或方法行为的工具。它允许在不改变原函数代码的情况下,动态地添加功能。装饰器本质上是一个闭包函数,接受一个函数作为参数并返回一个新的函数。

    2 装饰器的主要作用

    作用 描述
    代码复用 将通用功能(如日志记录、权限检查、性能测试等)封装到装饰器中,避免重复代码。
    增强函数功能 在不修改原函数的情况下,添加额外功能。
    简化代码 通过装饰器将横切关注点(如日志、缓存等)与核心逻辑分离,使代码更清晰。

    3 装饰器的常见应用场景

    场景 描述
    日志记录 自动记录函数的调用信息。
    权限验证 在函数执行前检查用户权限
    缓存 缓存函数的结果,避免重复计算。
    性能测试 测量函数的执行时间。
    事务管理 在数据库操作中自动管理事务。

    4 装饰器基本语法

    装饰器的构建有两种方式:传统语法和语法糖。

    语法糖(Syntactic Sugar)是指在编程语言中添加的某种语法特性,它并不改变语言的功能,但能使代码更易读、更简洁,从而提高开发效率和代码的可维护性。语法糖通常通过编译器或解释器转换为等价的标准语法,因此在运行时不会带来性能损失。

    4.1 传统方式

    def log_decorator(func):
        def log_inner(*args, **kwargs):
            print("方法调用前")
            result = func(*args, **kwargs)
            print("方法调用后")
            return result
    
        return log_inner
    
    
    def test(content):
        print(content)
    
    # 创建闭包对象,将要装饰的函数对象作为参数传入
    t = log_decorator(test)
    # 调用
    t("hello world")
    

    4.2 语法糖

    语法糖的形式一般常用

    # log_decorator 为外部函数
    def log_decorator(func):   
    	# log_inner 为内部函数
        def log_inner(*args, **kwargs):
            print("方法调用前")
            result = func(*args, **kwargs)
            print("方法调用后")
            return result
    	# 返回内部函数对象
        return log_inner
    
    
    @log_decorator
    def test(conetnt):
        print(conetnt)
    
    test("hello world")
    

    注意:装饰函数只能有一个参数,也就是外包函数只能传入一个参数,内部函数没有要求,一般与装饰函数的参数一致。

    通过传统函数,可以反推处语法糖的用法:

  • 将被装饰的函数作为参数传入到装饰函数中
  • 内部函数通过引用外部函数变量,并将自身形参传传递函数变量
  • 将内部函数作为对象返回
  • 5 使用案例

    5.1 无参无返回

    def log_decorator(func):
        def log_inner(*args, **kwargs):
            print("方法调用前")
            func(*args, **kwargs)
            print("方法调用后")
    
        return log_inner
    
    @log_decorator
    def test():
        print("hello world")
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # t = log_decorator(test)
        # # 调用
        # t()
    
        # 语法糖
        test()
    

    5.2 有参无返回

    def log_decorator(func):
        def log_inner(content):
            print("方法调用前")
            func(content)
            print("方法调用后")
    
        return log_inner
    
    @log_decorator
    def test(content):
        print(content)
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # t = log_decorator(test)
        # # 调用
        # t("hello world")
    
        # 语法糖
        test("hello world")
    

    5.3无参有返回

    def log_decorator(func):
        def log_inner():
            print("方法调用前")
            return func()
    
        return log_inner
    
    
    @log_decorator
    def test():
        return "hello world"
    
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # t = log_decorator(test)
        # # 调用
        # print(t("hello world"))
    
        # 语法糖
        print(test())
    

    5.4 有参有返回

    def log_decorator(func):
        def log_inner(content):
            print("方法调用前")
            return func(content)
    
        return log_inner
    
    
    @log_decorator
    def test(content):
        return content
    
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # t = log_decorator(test)
        # # 调用
        # print(t("hello world"))
    
        # 语法糖
        print(test("hello world"))
    

    5.5 可变参数

    def log_decorator(func):
        def log_inner(*args, **kwargs):
            print("方法调用前")
            func(*args, **kwargs)
            print("方法调用前后")
    
        return log_inner
    
    
    @log_decorator
    def test(*args, **kwargs):
        for arg in args:
            print(arg, end=' ')
        for key, value in kwargs.items():
            print((key, value), end=' ')
    
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # t = log_decorator(test)
        # # 调用
        # t("hello world", "python", name="Java", age=20)
    
        # 语法糖
        test("hello world", "python", name="Java", age=20)
    

    5.6 多个修饰器的使用

    def log_decorator(func):
        def log_inner(*args, **kwargs):
            print("日志记录中……")
            func(*args, **kwargs)
    
        return log_inner
    
    def cache_decorator(func):
        def cache_inner(*args, **kwargs):
            print("数据缓存中……")
            func(*args, **kwargs)
    
        return cache_inner
    
    
    @log_decorator
    @cache_decorator
    def test(*args, **kwargs):
        for arg in args:
            print(arg, end=' ')
        for key, value in kwargs.items():
            print((key, value), end=' ')
    
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # cache = cache_decorator(test)
        # log = log_decorator(cache)
        # # 调用,log 对象离调用对象最近,所以先返回
        # log("hello world", "python", name="Java", age=20)
    
        # 语法糖
        test("hello world", "python", name="Java", age=20)
    

    多个装饰器同时使用,如果是传统方式调用,则由内向外进行装饰,也就是根据返回对象离函数最近的返回,说白了,最后是谁调用的及返回的。

    cache = cache_decorator(test)
    log = log_decorator(cache)
    # 调用,log 对象离调用对象最近,所以先返回
    log("hello world", "python", name="Java", age=20)
    

    语法糖方式调用,则是从上往下先调用。

    @log_decorator
    @cache_decorator
    

    两者的返回结果一致,如下:

    日志记录中……
    数据缓存中……
    hello world python ('name', 'Java') ('age', 20) 
    

    5.7 带参数的装饰器

    修饰器本身也可以接受参数。此时需要定义一个返回修饰器的函数。

    def param_decorator(n):
        def log_decorator(func):
            def log_inner(*args, **kwargs):
                print("日志记录中……")
                for i in range(n):
                    print(f"日志记录{i + 1}")
                    func(*args, **kwargs)
                    print()
    
            return log_inner
    
        return log_decorator
    
    @param_decorator(3)
    def test(*args, **kwargs):
        for arg in args:
            print(arg, end=' ')
        for key, value in kwargs.items():
            print((key, value), end=' ')
    
    
    if __name__ == '__main__':
        # # 传统方式
        # # 创建闭包对象,将要装饰的函数对象作为参数传入
        # p = param_decorator(3)
        # t = p(test)
        # # 调用
        # t("hello world", "python", name="Java", age=20)
    
        # # 语法糖
        test("hello world", "python", name="Java", age=20)
    

    5.8 类装饰器

    def func_method(cls):
        def inner_method(self):
            return "这是装饰器返回的函数"
    
        cls.ext_method = inner_method
        return cls
    
    @func_method
    class TestClass:
        pass
    
    
    if __name__ == '__main__':
        # # 传统方式调用
        # # 装饰 TestClass类,返回修改后的类
        # test = func_method(TestClass)
        # # 实例化对象
        # t = test()
        # print(t.ext_method())
    
        # 语法糖
        test = TestClass()
        print(test.ext_method())
    

    作者:Karl_zhujt

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python装饰器深度解析

    发表回复