1. Python装饰器本质解析
装饰器本质上是一个高阶函数,它接收一个函数作为参数并返回一个新的函数。这种"函数包装函数"的特性,使得我们能在不修改原函数代码的前提下,为函数添加新功能。理解装饰器需要把握三个关键点:
- 函数在Python中是一等对象(可以赋值给变量、作为参数传递)
- 闭包特性(内部函数可以记住外部函数的作用域)
- @语法糖只是
func = decorator(func)的简写形式
典型的装饰器结构如下:
python复制def my_decorator(func):
def wrapper(*args, **kwargs):
# 前置处理
result = func(*args, **kwargs) # 执行原函数
# 后置处理
return result
return wrapper
2. 装饰器六大典型应用场景
2.1 日志记录与性能监控
这是装饰器最直观的应用。通过装饰器我们可以统一添加函数执行日志,而无需在每个函数中重复编写日志代码:
python复制import time
import logging
def log_execution(func):
def wrapper(*args, **kwargs):
start = time.time()
logging.info(f"开始执行 {func.__name__}")
result = func(*args, **kwargs)
end = time.time()
logging.info(f"{func.__name__} 执行完毕,耗时 {end-start:.2f}秒")
return result
return wrapper
@log_execution
def process_data(data):
# 数据处理逻辑
pass
注意:在生产环境中,建议使用functools.wraps装饰器来保留原函数的元信息,避免调试时出现混淆。
2.2 权限校验与访问控制
Web开发中经常需要对接口进行权限验证。装饰器可以优雅地实现这一需求:
python复制def admin_required(func):
def wrapper(user, *args, **kwargs):
if not user.is_admin:
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@admin_required
def delete_user(admin_user, target_user):
# 删除用户逻辑
pass
这种模式在Flask/Django等框架中广泛应用,比如@login_required装饰器。
2.3 输入验证与数据清洗
装饰器可以用于统一验证函数参数,确保输入数据的有效性:
python复制def validate_input(*validators):
def decorator(func):
def wrapper(*args, **kwargs):
for i, (arg, validator) in enumerate(zip(args, validators)):
if not validator(arg):
raise ValueError(f"参数{i}验证失败")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_input(lambda x: x > 0, lambda x: isinstance(x, str))
def calculate(price, product_name):
# 计算逻辑
pass
2.4 缓存与记忆化
对于计算密集型函数,可以使用装饰器实现缓存:
python复制from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
自定义缓存装饰器示例:
python复制def cache(func):
_cache = {}
def wrapper(*args):
if args in _cache:
return _cache[args]
result = func(*args)
_cache[args] = result
return result
return wrapper
2.5 重试机制
网络请求等可能失败的操作,可以通过装饰器实现自动重试:
python复制import time
from functools import wraps
def retry(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts == max_attempts:
raise
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=5, delay=2)
def call_external_api(url):
# API调用逻辑
pass
2.6 上下文管理
装饰器可以管理资源的获取和释放,类似于上下文管理器:
python复制def database_connection(func):
def wrapper(*args, **kwargs):
conn = connect_to_db()
try:
result = func(conn, *args, **kwargs)
return result
finally:
conn.close()
return wrapper
@database_connection
def query_data(conn, sql):
# 执行查询
return conn.execute(sql)
3. 装饰器进阶技巧与最佳实践
3.1 保留函数元信息
使用functools.wraps保持被装饰函数的__name__、__doc__等属性:
python复制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:.2f}s")
return result
return wrapper
3.2 带参数的装饰器
装饰器本身也可以接收参数,这需要额外嵌套一层函数:
python复制def repeat(n=1):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(n=3)
def greet(name):
print(f"Hello {name}")
3.3 类装饰器
装饰器不仅可以装饰函数,也可以装饰类:
python复制def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class Database:
def __init__(self):
print("数据库连接建立")
3.4 多个装饰器的执行顺序
装饰器从下往上执行,靠近函数定义的装饰器先执行:
python复制@decorator1
@decorator2
@decorator3
def func():
pass
# 等价于
func = decorator1(decorator2(decorator3(func)))
4. 何时避免使用装饰器
虽然装饰器很强大,但以下情况应谨慎使用:
- 性能敏感场景:每个装饰器都会增加一层函数调用开销
- 过度抽象:简单的功能直接写在函数内部可能更清晰
- 调试困难:多层装饰器可能导致调用栈难以追踪
- 改变函数签名:装饰器不应改变函数的输入输出约定
经验法则:当发现多个函数中有重复的样板代码时,就是考虑使用装饰器的好时机。
5. 常见问题与解决方案
5.1 装饰器导致类型提示失效
解决方案:使用typing模块的ParamSpec和TypeVar:
python复制from typing import TypeVar, Callable, ParamSpec
P = ParamSpec('P')
R = TypeVar('R')
def log_execution(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"执行 {func.__name__}")
return func(*args, **kwargs)
return wrapper
5.2 装饰器与方法一起使用
类方法装饰器需要处理self参数:
python复制def method_decorator(func):
def wrapper(self, *args, **kwargs):
print(f"调用 {func.__name__}")
return func(self, *args, **kwargs)
return wrapper
class MyClass:
@method_decorator
def my_method(self):
pass
5.3 装饰器与异步函数
处理async函数需要定义异步装饰器:
python复制def async_timer(func):
async def wrapper(*args, **kwargs):
start = time.time()
result = await func(*args, **kwargs)
end = time.time()
print(f"异步执行时间: {end-start:.2f}s")
return result
return wrapper
@async_timer
async def fetch_data():
# 异步获取数据
pass
在实际项目中,装饰器的最佳使用方式是将其视为一种代码组织模式,而不是炫技工具。当发现多个函数有相同的前置/后置处理逻辑时,装饰器就能大显身手了。