> 技术文档 > Python 装饰器

Python 装饰器


文章目录

  • 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_innerdef 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_decoratordef 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_decoratordef 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_decoratordef 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_decoratordef 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_decoratordef test(content): return contentif __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_decoratordef 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_innerdef cache_decorator(func): def cache_inner(*args, **kwargs): print(\"数据缓存中……\") func(*args, **kwargs) return cache_inner@log_decorator@cache_decoratordef 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_methodclass TestClass: passif __name__ == \'__main__\': # # 传统方式调用 # # 装饰 TestClass类,返回修改后的类 # test = func_method(TestClass) # # 实例化对象 # t = test() # print(t.ext_method()) # 语法糖 test = TestClass() print(test.ext_method())

南京印刷服务