在Python开发中,装饰器是一种强大的工具,允许开发者在不修改原函数代码的前提下,动态扩展功能。这一知识点广泛应用于日志记录、权限验证、性能监控等场景,但因其语法抽象,常成为学习难点。本文将深入解析装饰器的核心机制、实际应用及常见陷阱,帮助你高效掌握这一高级特性。
一、装饰器的核心概念与语法
装饰器本质是接收函数作为参数并返回新函数的语法糖,通过@符号简化调用。其核心逻辑基于Python的一等函数特性(函数可被赋值、传递和返回)。
基础语法:
def decorator_func(func):
def wrapper(*args, **kwargs):
print("函数执行前操作")
result = func(*args, **kwargs)
print("函数执行后操作")
return result
return wrapper
@decorator_func
def greet(name):
return f"Hello, {name}!
greet("Alice") # 输出:
# 函数执行前操作
# Hello, Alice!
# 函数执行后操作
关键点:
-
wrapper函数必须与原函数参数兼容(使用*args, **kwargs)。 -
装饰器会修改原函数的元信息(如
__name__),可通过functools.wraps修复:from functools import wraps def decorator_func(func): @wraps(func) def wrapper(*args, **kwargs): # ... return wrapper
二、实际应用场景与代码示例
1. 日志记录:自动记录函数执行时间与参数。
python
import time
from functools import wraps
def log_execution(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 耗时 {end_time - start_time:.4f}秒")
return result
return wrapper
@log_execution
def calculate_sum(n):
return sum(range(n))
calculate_sum(1000000) # 输出: 函数 calculate_sum 耗时 0.1234秒
1. 权限验证:检查用户权限后再执行函数。
def requires_admin(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user["role"] != "admin":
raise PermissionError("仅管理员可访问")
return func(user, *args, **kwargs)
return wrapper
@requires_admin
def delete_user(user, user_id):
print(f"删除用户 {user_id}")
delete_user({"role": "user"}, 123) # 抛出 PermissionError
2. 缓存结果:避免重复计算(如斐波那契数列)。
pythonCopy Code
from functools import lru_cache
@lru_cache(maxsize=128)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(30)) # 首次计算后,后续调用直接返回缓存结果
三、类装饰器与带参数的装饰器
1. 类装饰器:通过__call__方法实现。
pythonCopy Code
class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"调用次数: {self.calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # 输出: 调用次数: 1
say_hello() # 输出: 调用次数: 2
1. 带参数的装饰器:需嵌套函数。
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!
print(greet("Bob")) # 输出: Hello, Bob! 重复3次
四、常见误区与最佳实践
-
误区1:忽略
*args, **kwargs导致参数丢失。
解决方案:始终在wrapper中保留参数传递。 -
误区2:过度使用装饰器导致代码可读性下降。
最佳实践:- 为装饰器添加文档说明。
- 避免多层嵌套装饰器(如
@decorator1 @decorator2)。
-
性能注意:装饰器会增加函数调用开销,高频调用时需谨慎。