Python装饰器深度解析
文章目录
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