Python装饰器全面指南:从基础到高级应用
一、理解装饰器:概念与原理
1.1 前置知识:闭包函数
在深入理解装饰器之前,必须先掌握闭包函数的概念。闭包是装饰器的基石,理解了闭包,装饰器的工作原理就一目了然。
嵌套函数:在一个函数内部定义另一个函数,这就是嵌套函数。外层的称为外函数,内层的称为内函数。
闭包函数:当内函数使用了外函数的局部变量,并且外函数返回了这个内函数,就形成了闭包。
python
def nth_power(exponent): # 外函数
def exponent_of(base): # 内函数(闭包函数)
return base ** exponent # 使用了外函数的局部变量exponent
return exponent_of # 返回内函数
# 创建闭包
square = nth_power(2) # exponent=2
cube = nth_power(3) # exponent=3
print(square(2)) # 输出: 4 (2²)
print(cube(2)) # 输出: 8 (2³)
# 验证闭包特性
print(square.__closure__[0].cell_contents) # 输出: 2
闭包的关键特性是保存了外部函数的局部变量,即使外部函数已经执行完毕,闭包函数仍然可以访问这些变量。
1.2 装饰器的本质与定义
装饰器本质上是一种特殊的嵌套函数 ,它接收一个函数作为参数(被装饰的函数),并返回一个新的函数(装饰后的函数)。装饰器的最大价值在于不修改原函数代码的情况下,为函数添加新功能。
python
# 一个简单的装饰器示例
def decorator(func): # 接收一个函数作为参数
def wrapper(): # 定义一个新函数
print("Before function call")
func() # 调用原函数
print("After function call")
return wrapper # 返回新函数
def say_hello():
print("Hello!")
# 手动装饰
decorated_hello = decorator(say_hello)
decorated_hello()
# 输出:
# Before function call
# Hello!
# After function call
1.3 装饰器的基本工作机制
装饰器的工作原理基于Python的函数是一等公民的特性:函数可以作为参数传递,也可以作为返回值。
python
def simple_decorator(func):
def inner():
print("装饰器开始执行")
func()
print("装饰器执行结束")
return inner
def original_function():
print("原始函数执行")
# 装饰过程
original_function = simple_decorator(original_function)
# 此时original_function实际上是inner函数
original_function()
# 输出:
# 装饰器开始执行
# 原始函数执行
# 装饰器执行结束
二、装饰器的基本语法与应用
2.1 装饰器的基本用法
Python提供了简洁的装饰器语法糖 ,使用@符号简化装饰器的应用:
python
def simple_decorator(func):
def inner():
print("Before execution")
func()
print("After execution")
return inner
@simple_decorator # 语法糖,等价于:func = simple_decorator(func)
def func():
print("Function body")
func()
# 输出:
# Before execution
# Function body
# After execution
2.2 装饰带参数函数
当被装饰的函数有参数时,装饰器内部需要相应处理这些参数:
python
import time
def timer(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:.6f}秒")
return result
return wrapper
@timer
def sleep_function(seconds):
"""模拟耗时操作"""
time.sleep(seconds)
return f"睡了{seconds}秒"
@timer
def add_numbers(a, b):
"""加法函数"""
return a + b
print(sleep_function(1)) # 输出执行时间并返回结果
print(add_numbers(3, 5)) # 输出执行时间并返回结果
2.3 带参数的装饰器
装饰器本身也可以接收参数,这需要在装饰器外部再包裹一层函数:
python
def repeat(times):
"""重复执行函数的装饰器工厂"""
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for i in range(times):
print(f"第{i+1}次执行:")
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@repeat(times=3) # 装饰器带参数
def greet(name):
return f"Hello, {name}!"
print(greet("Alice"))
# 输出:
# 第1次执行:
# 第2次执行:
# 第3次执行:
# ['Hello, Alice!', 'Hello, Alice!', 'Hello, Alice!']
# 等价写法:
# greet = repeat(times=3)(greet)
2.4 装饰器嵌套与执行顺序
一个函数可以被多个装饰器装饰,装饰器的执行顺序是从下往上(从最靠近函数的装饰器开始):
python
def decorator_one(func):
def wrapper(*args, **kwargs):
print("装饰器一:开始")
result = func(*args, **kwargs)
print("装饰器一:结束")
return result
return wrapper
def decorator_two(func):
def wrapper(*args, **kwargs):
print("装饰器二:开始")
result = func(*args, **kwargs)
print("装饰器二:结束")
return result
return wrapper
@decorator_one
@decorator_two
def my_function():
print("原始函数执行")
my_function()
# 输出:
# 装饰器一:开始
# 装饰器二:开始
# 原始函数执行
# 装饰器二:结束
# 装饰器一:结束
# 等价于:
# my_function = decorator_one(decorator_two(my_function))
三、高级装饰器模式
3.1 类装饰器
除了函数,类也可以作为装饰器。类装饰器通过实现__call__方法,使实例可以像函数一样被调用:
python
class TimerDecorator:
"""类装饰器:计时器"""
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
import time
start_time = time.time()
result = self.func(*args, **kwargs)
end_time = time.time()
self.count += 1
print(f"{self.func.__name__} 第{self.count}次调用")
print(f"执行时间: {end_time - start_time:.6f}秒")
return result
@TimerDecorator
def expensive_operation(n):
"""模拟耗时计算"""
result = sum(i * i for i in range(n))
return result
print(expensive_operation(10000))
print(expensive_operation(20000)) # 第二次调用会显示不同的计数
3.2 保留函数元信息
使用装饰器后,原函数的元信息(如函数名、文档字符串)会被包装函数覆盖。使用functools.wraps可以解决这个问题:
python
import functools
def debug_decorator(func):
"""保留元信息的调试装饰器"""
@functools.wraps(func) # 保留原函数信息
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"参数: args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
@debug_decorator
def calculate_sum(a, b):
"""计算两个数的和"""
return a + b
print(calculate_sum(3, 4))
print(f"函数名: {calculate_sum.__name__}") # 输出: calculate_sum
print(f"文档字符串: {calculate_sum.__doc__}") # 输出: 计算两个数的和
3.3 可选参数装饰器
创建既支持带参数也支持不带参数的装饰器:
python
import functools
def optional_decorator(func=None, *, prefix="DEBUG"):
"""可选参数的装饰器"""
if func is None:
# 装饰器被带参数调用
return functools.partial(optional_decorator, prefix=prefix)
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[{prefix}] 调用: {func.__name__}")
return func(*args, **kwargs)
return wrapper
# 不带参数使用
@optional_decorator
def function_one():
print("函数一执行")
# 带参数使用
@optional_decorator(prefix="INFO")
def function_two():
print("函数二执行")
function_one() # 输出: [DEBUG] 调用: function_one
function_two() # 输出: [INFO] 调用: function_two
四、装饰器在实际项目中的应用
4.1 日志记录装饰器
记录函数调用信息,便于调试和监控:
python
import logging
import functools
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
def log_decorator(logger=None):
"""日志记录装饰器"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
nonlocal logger
if logger is None:
logger = logging.getLogger(func.__module__)
logger.info(f"开始执行: {func.__name__}")
logger.debug(f"参数: {args}, {kwargs}")
try:
result = func(*args, **kwargs)
logger.info(f"执行成功: {func.__name__}")
logger.debug(f"返回值: {result}")
return result
except Exception as e:
logger.error(f"执行失败: {func.__name__}, 错误: {e}")
raise
return wrapper
return decorator
@log_decorator()
def process_data(data):
"""处理数据函数"""
return [item * 2 for item in data]
result = process_data([1, 2, 3, 4, 5])
print(f"处理结果: {result}")
4.2 权限验证装饰器
在Web应用或API中控制访问权限:
python
class User:
"""用户类"""
def __init__(self, username, role):
self.username = username
self.role = role
def require_role(allowed_roles):
"""角色权限验证装饰器"""
def decorator(func):
def wrapper(user, *args, **kwargs):
if user.role not in allowed_roles:
raise PermissionError(
f"用户 {user.username} 没有权限执行此操作。"
f"需要角色: {allowed_roles}, 当前角色: {user.role}"
)
return func(user, *args, **kwargs)
return wrapper
return decorator
# 定义权限
ADMIN = 'admin'
EDITOR = 'editor'
VIEWER = 'viewer'
@require_role([ADMIN, EDITOR])
def edit_document(user, document_id):
"""编辑文档"""
return f"用户 {user.username} 正在编辑文档 {document_id}"
@require_role([ADMIN])
def delete_document(user, document_id):
"""删除文档"""
return f"用户 {user.username} 正在删除文档 {document_id}"
# 测试
admin = User("admin_user", ADMIN)
editor = User("editor_user", EDITOR)
viewer = User("viewer_user", VIEWER)
print(edit_document(editor, 123)) # 正常执行
print(edit_document(admin, 456)) # 正常执行
try:
print(edit_document(viewer, 789)) # 抛出权限错误
except PermissionError as e:
print(f"权限错误: {e}")