装饰器#
装饰器(Decorator)是 Python 中一个强大且优雅的特性,它允许你以非侵入式的方式为函数或方法添加功能,而无需修改原函数的代码。装饰器广泛用于日志记录、性能测试、权限验证等场景。
函数是一等公民#
在 Python 中,函数是一等公民(First-Class Object),意味着函数可以像变量一样被赋值、传递、返回。这为装饰器奠定了基础。
装饰器本质上是一个函数,它接受另一个函数作为输入,并返回一个新的函数(包装函数)。
# 函数赋值给变量
def greet(name):
return f"Hello, {name}!"
hello = greet # 函数赋值
print(hello("World")) # 输出: Hello, World!
# 函数作为参数传递
def execute(func, arg):
return func(arg)
print(execute(greet, "Python")) # 输出: Hello, Python!
Hello, World!
Hello, Python!
简单装饰器:无参数装饰器#
最基本的装饰器是一个函数,它包裹原函数,并在调用前后添加逻辑。
示例:添加打印日志的装饰器#
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"函数 {func.__name__} 开始执行...")
result = func(*args, **kwargs) # 调用原函数
print(f"函数 {func.__name__} 执行完成!")
return result
return wrapper
# 使用装饰器
@my_decorator # 等价于: greet = my_decorator(greet)
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 输出:
# 函数 greet 开始执行...
# Hello, Alice!
# 函数 greet 执行完成!
函数 greet 开始执行...
Hello, Alice!
函数 greet 执行完成!
上面代码说明:
my_decorator
是装饰器函数。wrapper
是内层包装函数,接收原函数的所有参数(*args, **kwargs
)。@my_decorator
语法糖:自动将greet
替换为my_decorator(greet)
。
带参数的装饰器#
有时装饰器需要接受参数(如配置选项)。这时,需要一个三层嵌套结构:外层函数接收装饰器参数,返回一个装饰器函数。
示例:可配置的重复执行装饰器#
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# 使用:重复执行 3 次
@repeat(times=3)
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Bob") # 输出: Hello, Bob! (三次)
Hello, Bob!
Hello, Bob!
Hello, Bob!
上面代码说明:
repeat(times)
:外层接收参数times
,返回装饰器decorator
。decorator(func)
:接收原函数,返回wrapper
。
类装饰器#
装饰器也可以是类,通过 __call__
方法实现。
示例:计数器类装饰器#
class Counter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数 {self.func.__name__} 已调用 {self.count} 次")
return self.func(*args, **kwargs)
@Counter
def multiply(x, y):
return x * y
print(multiply(2, 3)) # 输出: 函数 multiply 已调用 1 次 \n 6
print(multiply(4, 5)) # 输出: 函数 multiply 已调用 2 次 \n 20
函数 multiply 已调用 1 次
6
函数 multiply 已调用 2 次
20
上面代码说明:
类实例化时接收原函数(
__init__
)。每次调用实例时,触发
__call__
,执行原函数并添加逻辑。适合需要状态(如计数)的场景。
高级用法:使用 functools.wraps
#
直接使用 wrapper
会丢失原函数的元信息(如 __name__
、docstring
)。我们可以使用 functools.wraps
来修复。记住:始终使用 @wraps(func)
保持函数签名完整。
from functools import wraps
def my_decorator(func):
@wraps(func) # 复制原函数的元信息
def wrapper(*args, **kwargs):
print("执行前...")
result = func(*args, **kwargs)
print("执行后...")
return result
return wrapper
@my_decorator
def greet(name):
"""问候函数"""
return f"Hello, {name}!"
print(greet.__name__) # 输出: greet (而非 wrapper)
print(greet.__doc__) # 输出: 问候函数
greet
问候函数
实用示例:性能计时装饰器#
一个常见应用:测量函数执行时间。
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.4f} 秒")
return result
return wrapper
@timer
def slow_function(n):
time.sleep(1) # 模拟耗时操作
return sum(range(n))
print(slow_function(1000)) # 输出执行时间约 1 秒
slow_function 执行时间: 1.0051 秒
499500
多个装饰器和嵌套#
装饰器可以堆叠,按从下到上的顺序执行(自内而外)。
@timer
@my_decorator
def combined_func(name):
print(f"核心逻辑: {name}")
combined_func("Test")
# 先执行 my_decorator 的逻辑,然后 timer 的
执行前...
核心逻辑: Test
执行后...
combined_func 执行时间: 0.0001 秒
多个装饰器叠加顺序#
@d2
@d1
def func(): ...
等价于 func = d2(d1(func))。执行顺序:d1 先包,但 d2 的外层 wrapper 先运行。