Python 第十四节 装饰器使用详细说明及注意事项
·
前言
装饰器是Python中一种强大的语法特性,它允许在不修改原函数代码的情况下,为函数添加新的功能。装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。
应用场景
- 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
- 性能分析: 可以使用装饰器来测量函数的执行时间。
- 权限控制: 装饰器可用于限制对某些函数的访问权限。
- 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。
内置装饰器
@staticmethod: 将方法定义为静态方法,不需要实例化类即可调用。@classmethod: 将方法定义为类方法,第一个参数是类本身(通常命名为 cls)。@property: 将方法转换为属性,使其可以像属性一样访问。
1. 基本语法
def decorator(func):
def wrapper(*args, **kwargs):
# 在调用原函数前的操作
result = func(*args, **kwargs)
# 在调用原函数后的操作
return result
return wrapper
@decorator
def my_function():
pass
2. 装饰器案例分析
2.1 计时装饰器
import time
import functools
def timer(func):
"""计算函数执行时间的装饰器"""
@functools.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
@timer
def fibonacci(n):
"""计算斐波那契数列"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 使用
print(fibonacci(10)) # 55
2.2 日志记录装饰器
import functools
from datetime import datetime
def logger(func):
"""记录函数调用信息的装饰器"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
call_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{call_time}] 调用函数: {func.__name__}")
print(f" 参数: args={args}, kwargs={kwargs}")
try:
result = func(*args, **kwargs)
print(f" 返回值: {result}")
return result
except Exception as e:
print(f" 异常: {e}")
raise
return wrapper
@logger
def divide(a, b):
return a / b
# 使用
divide(10, 2)
divide(10, 0) # 会记录异常
2.3 参数化装饰器
import functools
def repeat(num_times):
"""重复执行函数的装饰器"""
def decorator_repeat(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
# 使用
greet("Andy")
# 打印 3次
# Hello Andy
# Hello Andy
# Hello Andy
2.4 类装饰器
类装饰器可以用于:
添加/修改类的方法或属性
拦截实例化过程
实现单例模式、日志记录、权限检查等功能
类装饰器有两种常见形式:
函数形式的类装饰器(接收类作为参数,返回新类)
类形式的类装饰器(实现 __call__ 方法,使其可调用)
class CountCalls:
"""统计函数调用次数的类装饰器"""
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"调用 {self.func.__name__} 第 {self.num_calls} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
# 使用
say_hello()
say_hello()
print(f"总调用次数: {say_hello.num_calls}")
2.5 权限验证装饰器
import functools
def require_role(required_role):
"""基于角色的权限验证装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('role') != required_role:
raise PermissionError(f"需要 {required_role} 权限,当前用户权限: {user.get('role')}")
return func(user, *args, **kwargs)
return wrapper
return decorator
# 用户数据
users = [
{'name': 'Alice', 'role': 'admin'},
{'name': 'Bob', 'role': 'user'}
]
@require_role('admin')
def delete_user(current_user, username):
print(f"{current_user['name']} 删除了用户 {username}")
# 使用
try:
delete_user(users[0], "Charlie") # 成功
delete_user(users[1], "Charlie") # 失败
except PermissionError as e:
print(f"权限错误: {e}")
2.6 缓存装饰器
import functools
from typing import Any
def cache(func):
"""简单的缓存装饰器"""
cached_results = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 创建缓存键
key = str(args) + str(sorted(kwargs.items()))
if key in cached_results:
print(f"从缓存中获取 {func.__name__}{args} 的结果")
return cached_results[key]
result = func(*args, **kwargs)
cached_results[key] = result
print(f"计算 {func.__name__}{args} 的结果并缓存")
return result
return wrapper
@cache
def expensive_operation(x, y):
time.sleep(1) # 模拟耗时操作
return x * y
# 使用
print(expensive_operation(3, 4)) # 第一次计算
print(expensive_operation(3, 4)) # 从缓存获取
3. 多个装饰器的使用
def decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器1 - 前")
result = func(*args, **kwargs)
print("装饰器1 - 后")
return result
return wrapper
def decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器2 - 前")
result = func(*args, **kwargs)
print("装饰器2 - 后")
return result
return wrapper
@decorator1
@decorator2
def example():
print("原始函数")
# 使用
example()
# 输出:
# 装饰器1 - 前
# 装饰器2 - 前
# 原始函数
# 装饰器2 - 后
# 装饰器1 - 后
4. 使用注意事项
4.1 保留元信息
错误做法:
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_function():
"""这是一个测试函数"""
pass
print(my_function.__name__) # 输出: wrapper
print(my_function.__doc__) # 输出: None
正确做法:
import functools
def good_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@good_decorator
def my_function():
"""这是一个测试函数"""
pass
print(my_function.__name__) # 输出: my_function
print(my_function.__doc__) # 输出: 这是一个测试函数
4.2 处理带参数的装饰器
import functools
def smart_decorator(condition=True):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if condition:
print("装饰器功能启用")
else:
print("装饰器功能禁用")
return func(*args, **kwargs)
return wrapper
return decorator
# 使用
@smart_decorator(condition=True)
def function1():
print("函数1执行")
@smart_decorator(condition=False)
def function2():
print("函数2执行")
4.3 避免装饰器副作用
def safe_decorator(func):
"""安全的装饰器,处理异常"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"函数 {func.__name__} 执行出错: {e}")
# 可以选择返回默认值或重新抛出异常
return None
return wrapper
@safe_decorator
def risky_operation():
return 1 / 0 # 这会引发异常
result = risky_operation() # 不会崩溃,会打印错误信息
5. 实际应用场景
5.1 Web框架中的路由装饰器
class Router:
def __init__(self):
self.routes = {}
def route(self, path):
def decorator(func):
self.routes[path] = func
return func
return decorator
router = Router()
@router.route("/home")
def home():
return "Home Page"
@router.route("/about")
def about():
return "About Page"
# 使用
print(router.routes) # 输出: {'/home': <function home>, '/about': <function about>}
5.2 数据库事务装饰器
def transaction(db_connection):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
db_connection.commit()
print("事务提交成功")
return result
except Exception as e:
db_connection.rollback()
print(f"事务回滚: {e}")
raise
return wrapper
return decorator
总结
装饰器是用途:
增强函数功能:添加日志、计时、缓存等功能
代码复用:将通用功能抽象为装饰器
保持代码整洁:避免修改原函数代码
实现AOP:面向切面编程
最佳实践:
- 始终使用
@functools.wraps保留函数元信息 - 为装饰器编写清晰的文档字符串
- 考虑装饰器的性能和副作用
- 在团队中建立装饰器使用规范
通过合理使用装饰器,可以大幅提高代码的可维护性和可读性。
更多推荐
所有评论(0)