名词(术语)了解–装饰器(decorator)
装饰器基础概念
装饰器是Python中的一个重要特性,它允许我们在不修改原有代码的情况下,为函数或类添加新的功能。本质上,装饰器是一个返回函数的函数。
1. 基本装饰器
最简单的装饰器形式:
def simple_decorator(func):
def wrapper():
print("开始执行")
func()
print("执行结束")
return wrapper
@simple_decorator
def hello():
print("Hello, World!")
# 调用函数
hello()
# 输出:
# 开始执行
# Hello, World!
# 执行结束
2. 带参数的装饰器
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"你好, {name}!")
greet("张三")
# 输出三次:你好, 张三!
3. 保留函数元数据的装饰器
from functools import wraps
def log_decorator(func):
@wraps(func) # 保留原函数的元数据
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_decorator
def add(a, b):
"""计算两个数的和"""
return a + b
print(add.__name__) # 输出: add
print(add.__doc__) # 输出: 计算两个数的和
4. 类装饰器
class Timer:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start = time.time()
result = self.func(*args, **kwargs)
end = time.time()
print(f"函数 {self.func.__name__} 执行时间: {end - start:.2f} 秒")
return result
@Timer
def slow_function():
import time
time.sleep(1)
print("函数执行完成")
slow_function()
5. 实用装饰器示例
5.1 缓存装饰器
def cache_decorator(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@cache_decorator
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 计算会很快
5.2 错误重试装饰器
import time
def retry(max_attempts=3, delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"尝试失败,{delay}秒后重试...")
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=1)
def unstable_function():
import random
if random.random() < 0.7:
raise Exception("随机错误")
return "成功"
5.3日志装饰器
```python
import logging
def log_function_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"调用函数: {func.__name__}")
try:
result = func(*args, **kwargs)
logging.info(f"函数 {func.__name__} 执行成功")
return result
except Exception as e:
logging.error(f"函数 {func.__name__} 执行失败: {str(e)}")
raise
return wrapper
@log_function_call
def divide(a, b):
return a / b
```
6. 内置装饰器的使用
class Person:
def __init__(self):
self._name = ""
@property
def name(self):
"""获取姓名"""
return self._name
@name.setter
def name(self, value):
"""设置姓名"""
self._name = value
@staticmethod
def static_method():
"""静态方法"""
print("这是静态方法")
@classmethod
def class_method(cls):
"""类方法"""
print("这是类方法")
7. 装饰器的使用建议
-
单一职责原则:每个装饰器应该只做一件事。
-
使用functools.wraps:保留原函数的元数据。
-
参数验证:在装饰器中进行参数验证。
-
错误处理:适当的异常处理和日志记录。
-
文档化:为装饰器添加清晰的文档字符串。
def validate_params(func):
"""参数验证装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
# 参数验证逻辑
if not args and not kwargs:
raise ValueError("函数参数不能为空")
return func(*args, **kwargs)
return wrapper
@validate_params
def process_data(data):
"""处理数据的函数"""
return data
8. 装饰器的注意事项
- 顺序很重要:多个装饰器的执行顺序是从下到上的。
@decorator1
@decorator2
def function():
pass
# 相当于: decorator1(decorator2(function))
-
使用functools.wraps:为了保留原函数的元数据,建议使用
@functools.wraps
。 -
类方法装饰器:在类中使用装饰器时要注意
self
参数:
class MyClass:
@timer
def my_method(self, x):
return x * 2
- 装饰器的性能影响:装饰器会增加函数调用的开销,在性能敏感的场景要谨慎使用。
9. 多重装饰器
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>
装饰器是Python中非常强大的特性,它们可以帮助我们:
- 减少代码重复
- 提高代码的可读性
- 实现横切关注点(如日志、性能监控等)
- 动态修改函数或类的行为
装饰器扩展
1. 描述符协议
描述符是实现了特定协议的类,可以用来自定义属性的访问方式:
class TypedProperty: def __init__(self, name, type_): self.name = name self.type = type_ def __get__(self, instance, cls): if instance is None: return self return instance.__dict__.get(self.name) def __set__(self, instance, value): if not isinstance(value, self.type): raise TypeError(f'期望类型 {self.type}') instance.__dict__[self.name] = value class Person: name = TypedProperty('name', str) age = TypedProperty('age', int) def __init__(self, name: str, age: int): self.name = name self.age = age # 使用 person = Person("张三", 25) try: person.age = "invalid" # 将引发TypeError except TypeError as e: print(f"错误: {e}")
2. 异步装饰器
支持异步函数的装饰器:
import asyncio import functools def async_retry(retries=3, delay=1): def decorator(func): @functools.wraps(func) async def wrapper(*args, **kwargs): for attempt in range(retries): try: return await func(*args, **kwargs) except Exception as e: if attempt == retries - 1: raise print(f"尝试 {attempt + 1} 失败,等待 {delay} 秒...") await asyncio.sleep(delay) return None return wrapper return decorator @async_retry(retries=3, delay=1) async def fetch_data(): # 模拟异步操作 import random if random.random() < 0.7: raise ConnectionError("连接错误") return "数据获取成功" # 使用示例 async def main(): try: result = await fetch_data() print(result) except ConnectionError as e: print(f"最终失败: {e}") # asyncio.run(main())
3. 上下文装饰器
结合上下文管理器的装饰器:
from contextlib import contextmanager import time class Timer: def __init__(self, name): self.name = name def __enter__(self): self.start = time.time() return self def __exit__(self, *args): self.end = time.time() self.duration = self.end - self.start print(f"{self.name} 耗时: {self.duration:.2f} 秒") def with_timer(func): def wrapper(*args, **kwargs): with Timer(func.__name__): return func(*args, **kwargs) return wrapper @with_timer def slow_operation(): time.sleep(1) return "操作完成"
4. 参数验证装饰器
高级参数验证示例:
from typing import get_type_hints import functools def validate_types(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 获取函数的类型注解 hints = get_type_hints(func) # 检查位置参数 for arg, value in zip(func.__code__.co_varnames, args): if arg in hints: if not isinstance(value, hints[arg]): raise TypeError( f"参数 {arg} 必须是 {hints[arg].__name__} 类型" ) # 检查关键字参数 for key, value in kwargs.items(): if key in hints: if not isinstance(value, hints[key]): raise TypeError( f"参数 {key} 必须是 {hints[key].__name__} 类型" ) return func(*args, **kwargs) return wrapper @validate_types def process_user(name: str, age: int, scores: list = None): return f"用户 {name}, 年龄 {age}, 分数 {scores}" # 使用示例 try: result = process_user("张三", "25") # 将引发TypeError except TypeError as e: print(f"错误: {e}")
5. 装饰器工厂
动态创建装饰器的高级模式:
def create_validator(validation_func, error_message): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if not validation_func(*args, **kwargs): raise ValueError(error_message) return func(*args, **kwargs) return wrapper return decorator # 创建具体的验证装饰器 def positive_args(func): return create_validator( lambda *args, **kwargs: all(arg > 0 for arg in args), "所有参数必须为正数" )(func) def non_empty_string(func): return create_validator( lambda s: isinstance(s, str) and bool(s.strip()), "字符串不能为空" )(func) # 使用示例 @positive_args def calculate_area(width, height): return width * height @non_empty_string def greet(name): return f"Hello, {name}!"
6. 单例模式装饰器
def singleton(cls): instances = {} @functools.wraps(cls) def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class Database: def __init__(self): print("初始化数据库连接...") def query(self): print("执行查询...") # 使用 db1 = Database() db2 = Database() print(db1 is db2) # 输出: True
7. 属性缓存装饰器
def cached_property(func): name = func.__name__ @property @functools.wraps(func) def wrapper(self): if not hasattr(self, f"_cached_{name}"): setattr(self, f"_cached_{name}", func(self)) return getattr(self, f"_cached_{name}") return wrapper class ExpensiveComputation: def __init__(self, data): self.data = data @cached_property def processed_data(self): print("处理数据...") return sum(self.data) # 使用 comp = ExpensiveComputation([1, 2, 3, 4, 5]) print(comp.processed_data) # 首次计算 print(comp.processed_data) # 使用缓存
8. 方法装饰器与类装饰器的结合
def log_methods(cls): """记录类中所有方法调用的装饰器""" class_dict = cls.__dict__.copy() for name, method in class_dict.items(): if callable(method): setattr(cls, name, log_method(method)) return cls def log_method(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): print(f"调用方法 {method.__name__}") result = method(self, *args, **kwargs) print(f"方法 {method.__name__} 返回 {result}") return result return wrapper @log_methods class Calculator: def add(self, x, y): return x + y def multiply(self, x, y): return x * y # 使用 calc = Calculator() calc.add(2, 3) calc.multiply(4, 5)
9. 装饰器的错误处理和调试
import sys import traceback def debug(func): @functools.wraps(func) def wrapper(*args, **kwargs): try: print(f"调用 {func.__name__}") print(f"参数: {args}, {kwargs}") result = func(*args, **kwargs) print(f"返回: {result}") return result except Exception as e: print(f"错误: {e}") print("调用栈:") traceback.print_exc(file=sys.stdout) raise return wrapper @debug def divide(a, b): return a / b
这些高级特性和扩展使得装饰器成为Python中非常强大和灵活的工具。它们可以用于:
- 代码复用和模块化
- 横切关注点的处理
- 元编程和代码生成
- 性能优化和缓存
- 参数验证和类型检查
- 设计模式实现
在使用这些高级特性时,需要注意:
- 保持代码的可读性和可维护性
- 适当的错误处理和异常传播
- 性能影响的考虑
- 文档的完整性
- 测试的覆盖率