什么是 Python 装饰器?
装饰器(Decorator)是 Python 中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心概念
在深入学习之前,我们需要理解几个关键概念:
- 函数是一等公民:在 Python 中,函数可以作为参数传递,也可以作为返回值。
- 闭包:装饰器依赖于闭包的概念,即内部函数可以访问外部函数的变量。
- 语法糖:
@符号是装饰器的语法糖,它让装饰器的使用更加简洁。
基础装饰器的实现
最简单的装饰器示例
让我们从一个最基础的装饰器开始,它会在函数执行前后打印日志:
def logger_decorator(func):
def wrapper(*args, **kwargs):
print(f"开始执行函数: {func.__name__}")
result = func(*args, **kwargs)
print(f"函数执行完成: {func.__name__}")
return result
return wrapper
# 使用装饰器
@logger_decorator
def add(a, b):
return a + b
# 测试
result = add(3, 5)
print(f"结果: {result}")
输出结果:
开始执行函数: add
函数执行完成: add
结果: 8
带参数的装饰器
有时候我们需要让装饰器本身接受参数,这需要创建一个返回装饰器的函数:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
print(f"第 {i+1} 次执行")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
# 使用带参数的装饰器
@repeat_decorator(3)
def greet(name):
print(f"你好, {name}!")
return f"问候了 {name}"
# 测试
greet("Alice")
输出结果:
第 1 次执行
你好, Alice!
第 2 次执行
你好, Alice!
第 3 次执行
你好, Alice!
装饰器的高级用法
保留原函数的元信息
默认情况下,装饰器会覆盖原函数的 __name__、__doc__ 等属性。使用 functools.wraps 可以解决这个问题:
import functools
def preserve_info_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""这是包装函数的文档字符串"""
print("执行装饰器逻辑")
return func(*args, **kwargs)
return wrapper
@preserve_info_decorator
def example_function():
"""这是原函数的文档字符串"""
return "Hello"
print(f"函数名: {example_function.__name__}")
print(f"文档字符串: {example_function.__doc__}")
输出结果:
函数名: example_function
文档字符串: 这是原函数的文档字符串
类装饰器
除了函数装饰器,Python 还支持类装饰器:
class CountCalls:
def __init__(self, func):
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs):
self.call_count += 1
print(f"函数被调用了 {self.call_count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
return "Hello!"
# 测试
say_hello()
say_hello()
say_hello()
输出结果:
函数被调用了 1 次
函数被调用了 2 次
函数被调用了 3 次
实际应用场景
1. 性能监控装饰器
import time
import functools
def timing_decorator(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
@timing_decorator
def slow_function():
time.sleep(1)
return "完成"
# 测试
slow_function()
2. 权限验证装饰器
def require_admin(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.get('role') != 'admin':
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@require_admin
def delete_user(user, target_user):
print(f"用户 {user['name']} 删除了用户 {target_user}")
# 测试
admin_user = {'name': 'Alice', 'role': 'admin'}
regular_user = {'name': 'Bob', 'role': 'user'}
# 这会正常执行
delete_user(admin_user, "Charlie")
# 这会抛出异常
try:
delete_user(regular_user, "David")
except PermissionError as e:
print(f"错误: {e}")
3. 缓存装饰器(记忆化)
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
print(f"从缓存中获取结果: {args}")
return cache[args]
result = func(*args)
cache[args] = result
print(f"计算新结果: {args} = {result}")
return result
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 测试
print("计算 fibonacci(5):")
print(fibonacci(5))
装饰器的执行顺序
当多个装饰器堆叠使用时,它们的执行顺序是从下往上的:
def decorator1(func):
def wrapper(*args, **kwargs):
print("装饰器1 - 前")
result = func(*args, **kwargs)
print("装饰器1 - 后")
return result
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print("装饰器2 - 前")
result = func(*args, **kwargs)
print("装饰器2 - 后")
return result
return wrapper
@decorator1
@decorator2
def my_function():
print("执行原函数")
# 测试
my_function()
输出结果:
装饰器1 - 前
装饰器2 - 前
执行原函数
装饰器2 - 后
装饰器1 - 后
常见陷阱和注意事项
1. 装饰器的参数传递问题
def debug_decorator(func):
def wrapper(*args, **kwargs):
print(f"参数: args={args}, kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@debug_decorator
def complex_function(a, b, c=10, d=20):
return a + b + c + d
# 正确使用
complex_function(1, 2, c=3, d=4)
2. 类方法的装饰器
class MyClass:
def __init__(self):
self.value = 0
@logger_decorator # 使用之前定义的logger装饰器
def increment(self, amount):
self.value += amount
return self.value
obj = MyClass()
obj.increment(5)
最佳实践
- 保持装饰器的单一职责:每个装饰器应该只做一件事
- 使用 functools.wraps:始终保留原函数的元信息
- 文档化:为装饰器和被装饰的函数提供清晰的文档
- 避免过度使用:装饰器很强大,但不是所有问题都需要装饰器
- 测试:确保装饰器在各种情况下都能正常工作
总结
Python 装饰器是一个强大而优雅的特性,它能够:
- 在不修改原函数的情况下添加功能
- 保持代码的简洁和可读性
- 实现关注点分离
- 提高代码的复用性
通过掌握装饰器,你可以写出更加 Pythonic 的代码,让你的程序更加模块化和易于维护。从简单的日志记录到复杂的权限控制,装饰器都能派上用场。记住,好的装饰器应该像一个透明的层,增强函数的功能而不改变其本质。
