什么是装饰器?
装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。
装饰器的核心思想是遵循开放封闭原则:对扩展开放,对修改封闭。这意味着你可以通过装饰器来扩展代码的功能,而不需要修改原有的代码实现。
基础装饰器的实现
让我们从一个简单的例子开始。假设我们有一个需要记录执行时间的函数:
import time
def timer_decorator(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_decorator
def calculate_sum(n):
total = 0
for i in range(n):
total += i
return total
# 使用示例
result = calculate_sum(1000000)
print(f"计算结果: {result}")
在这个例子中:
timer_decorator是一个装饰器函数- 它接收一个函数
func作为参数 - 返回一个新的函数
wrapper,这个函数在调用原函数前后添加了计时功能 - 使用
@timer_decorator语法将装饰器应用到calculate_sum函数上
带参数的装饰器
有时候我们需要让装饰器本身可以接受参数。这需要创建一个返回装饰器的函数:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat_decorator(3)
def greet(name):
print(f"你好, {name}!")
return name
# 使用示例
greet("小明")
输出:
你好, 小明!
你好, 小明!
你好, 小明!
类装饰器
除了函数装饰器,Python还支持类装饰器。类装饰器需要实现 __call__ 方法:
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.func.__name__} 被调用了 {self.call_count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
# 使用示例
say_hello()
say_hello()
say_hello()
输出:
函数 say_hello 被调用了 1 次
Hello!
函数 say_hello 被调用了 2 次
Hello!
函数 say_hello 被调用了 3 次
Hello!
内置装饰器
Python提供了一些内置装饰器,最常用的是:
1. @property
将方法转换为属性:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("半径必须大于0")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
# 使用示例
c = Circle(5)
print(c.radius) # 5
print(c.area) # 78.53975
c.radius = 10
print(c.area) # 314.159
2. @classmethod
将方法转换为类方法:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
from datetime import datetime
current_year = datetime.now().year
age = current_year - birth_year
return cls(name, age)
# 使用示例
p1 = Person("Alice", 25)
p2 = Person.from_birth_year("Bob", 1995)
print(p2.name, p2.age)
3. @staticmethod
将方法转换为静态方法:
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
# 使用示例
print(MathUtils.add(3, 5)) # 8
print(MathUtils.multiply(3, 5)) # 15
装饰器的高级应用
1. 多个装饰器的组合
多个装饰器可以堆叠使用,执行顺序是从下往上:
def bold(func):
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def italic(func):
def wrapper():
return "<i>" + func() + "</i>"
return wrapper
@bold
@italic
def hello():
return "Hello World"
print(hello()) # <b><i>Hello World</i></b>
2. 使用functools.wraps
为了保留原函数的元信息(如函数名、文档字符串等),应该使用 functools.wraps:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""这是一个包装函数"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""这是一个示例函数"""
pass
print(example.__name__) # example (而不是wrapper)
print(example.__doc__) # 这是一个示例函数
3. 装饰器的实际应用场景
缓存/记忆化
from functools import wraps
import time
def cache(seconds):
def decorator(func):
cache_dict = {}
@wraps(func)
def wrapper(*args):
current_time = time.time()
# 检查缓存是否存在且未过期
if args in cache_dict:
result, timestamp = cache_dict[args]
if current_time - timestamp < seconds:
print("使用缓存结果")
return result
# 执行函数并缓存结果
result = func(*args)
cache_dict[args] = (result, current_time)
print("计算新结果")
return result
return wrapper
return decorator
@cache(5) # 缓存5秒
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 使用示例
print(fibonacci(10)) # 计算新结果
time.sleep(2)
print(fibonacci(10)) # 使用缓存结果
time.sleep(4)
print(fibonacci(10)) # 计算新结果
权限验证
def require_admin(func):
@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") # 正常执行
# delete_user(regular_user, "Charlie") # 抛出PermissionError
重试机制
def retry(max_attempts=3, delay=1):
import time
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 e
print(f"尝试 {attempts}/{max_attempts} 失败,{delay}秒后重试...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_function():
import random
if random.random() < 0.7:
raise Exception("随机失败")
return "成功"
# 使用示例
try:
result = unstable_function()
print(result)
except Exception as e:
print(f"最终失败: {e}")
装饰器的最佳实践
- 保持装饰器简单:每个装饰器应该只做一件事
- 使用functools.wraps:保留原函数的元信息
- 考虑装饰器的顺序:多个装饰器时注意执行顺序
- 为装饰器编写文档:说明其功能和参数
- 测试装饰器:确保装饰器在各种情况下都能正常工作
总结
装饰器是Python中非常强大的工具,它可以帮助我们:
- 在不修改原函数的情况下添加功能
- 保持代码的简洁和可维护性
- 实现代码复用
- 遵循开放封闭原则
通过掌握装饰器,你可以写出更加优雅和Pythonic的代码。从简单的日志记录到复杂的缓存机制,装饰器都能派上用场。记住,装饰器的核心思想是包装和扩展,而不是修改。
在实际项目中,装饰器常用于:
- 日志记录和性能监控
- 权限验证和访问控制
- 缓存和记忆化
- 输入验证和错误处理
- 事务管理
- 路由注册(在Web框架中)
希望这篇文章能帮助你深入理解Python装饰器的概念和应用!
