Python 零基础入门系列(十)答案:闭包和装饰器实战答案讲解
·
🐍 Python 零基础入门系列(十)答案:闭包和装饰器实战答案讲解
📢 系列说明:
这是《Python 零基础入门系列》第十篇的配套答案讲解!请先独立完成第十篇的3个实战练习,再参考答案进行核对。📅 更新时间:2026 年 3 月 27 日
🎯 本篇内容:第十篇实战练习的完整参考答案 + 详细讲解 + 常见错误分析
⏱️ 预计阅读时间:50-60 分钟
📌 前置知识:已完成第十篇教程和实战练习
✍️ 作者:书到用时方恨少!
📋 完整目录
┌─────────────────────────────────────────────────────────────┐
│ 本篇博客内容导航 │
├─────────────────────────────────────────────────────────────┤
│ 第一部分:练习一答案 - 日志装饰器 │
│ ├── 1. 完整参考代码 │
│ ├── 2. 逐行代码讲解 │
│ └── 3. 常见错误分析 │
├─────────────────────────────────────────────────────────────┤
│ 第二部分:练习二答案 - 计时装饰器 │
│ ├── 4. 完整参考代码 │
│ ├── 5. 逐行代码讲解 │
│ └── 6. 常见错误分析 │
├─────────────────────────────────────────────────────────────┤
│ 第三部分:练习三答案 - 缓存装饰器 │
│ ├── 7. 完整参考代码 │
│ ├── 8. 逐行代码讲解 │
│ └── 9. 常见错误分析 │
├─────────────────────────────────────────────────────────────┤
│ 第四部分:优化建议与扩展 │
│ ├── 10. 代码优化建议 │
│ └── 11. 扩展学习方向 │
└─────────────────────────────────────────────────────────────┘
第一部分:练习一答案 - 日志装饰器
1. 完整参考代码
# ============================================================
# 练习一:日志装饰器 - 完整参考答案
# ============================================================
import time
from datetime import datetime
from functools import wraps
# ============================================================
# 日志级别定义
# ============================================================
LOG_LEVELS = {
'DEBUG': 0,
'INFO': 1,
'WARNING': 2,
'ERROR': 3
}
# ============================================================
# 日志装饰器
# ============================================================
def log_decorator(level="INFO", log_file=None):
"""
日志装饰器
Args:
level: 日志级别 (DEBUG/INFO/WARNING/ERROR)
log_file: 日志文件路径(可选,None 表示只输出到控制台)
Returns:
装饰器函数
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 获取当前时间
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 记录开始时间
start_time = time.time()
# 准备日志信息
log_messages = []
log_messages.append(f"[{timestamp}] [{level}] 开始执行 {func.__name__}")
log_messages.append(f"[{timestamp}] [{level}] 参数:args={args}, kwargs={kwargs}")
try:
# 调用原函数
result = func(*args, **kwargs)
# 记录结束时间
end_time = time.time()
exec_time = end_time - start_time
# 记录成功日志
log_messages.append(f"[{timestamp}] [{level}] 返回值:{result}")
log_messages.append(f"[{timestamp}] [{level}] 执行时间:{exec_time:.4f}秒")
log_messages.append(f"[{timestamp}] [{level}] 执行完成 {func.__name__}")
# 输出日志
_write_logs(log_messages, log_file)
return result
except Exception as e:
# 记录结束时间
end_time = time.time()
exec_time = end_time - start_time
# 记录错误日志
error_level = "ERROR"
log_messages.append(f"[{timestamp}] [{error_level}] 异常:{type(e).__name__}: {e}")
log_messages.append(f"[{timestamp}] [{error_level}] 执行时间:{exec_time:.4f}秒")
log_messages.append(f"[{timestamp}] [{error_level}] 执行失败 {func.__name__}")
# 输出日志
_write_logs(log_messages, log_file)
# 重新抛出异常
raise
return wrapper
return decorator
def _write_logs(messages, log_file=None):
"""
写入日志
Args:
messages: 日志消息列表
log_file: 日志文件路径
"""
for message in messages:
# 输出到控制台
print(message)
# 输出到文件
if log_file:
with open(log_file, 'a', encoding='utf-8') as f:
f.write(message + '\n')
# ============================================================
# 测试代码
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("=== 日志装饰器测试 ===")
print("=" * 60)
# 测试 1:基础日志
print("\n【测试 1】基础日志装饰器")
print("-" * 60)
@log_decorator()
def add(a, b):
"""加法函数"""
return a + b
result = add(3, 5)
print(f"最终结果:{result}")
# 测试 2:不同日志级别
print("\n【测试 2】不同日志级别")
print("-" * 60)
@log_decorator(level="DEBUG")
def divide(a, b):
return a / b
@log_decorator(level="WARNING")
def multiply(a, b):
return a * b
divide(10, 2)
multiply(3, 4)
# 测试 3:日志文件
print("\n【测试 3】日志文件输出")
print("-" * 60)
@log_decorator(level="INFO", log_file="test.log")
def greet(name):
return f"Hello, {name}!"
greet("小明")
greet("小红")
print("\n日志已写入 test.log 文件")
# 测试 4:异常处理
print("\n【测试 4】异常处理日志")
print("-" * 60)
@log_decorator(level="ERROR")
def risky_divide(a, b):
return a / b
try:
risky_divide(10, 0)
except Exception as e:
print(f"捕获异常:{e}")
print("\n" + "=" * 60)
print("=== 测试完成 ===")
print("=" * 60)
2. 逐行代码讲解
📖 日志级别定义
LOG_LEVELS = {
'DEBUG': 0,
'INFO': 1,
'WARNING': 2,
'ERROR': 3
}
讲解:
- 定义日志级别字典,便于扩展级别过滤功能
- 数值越小级别越低,可用于过滤日志输出
📖 装饰器结构
def log_decorator(level="INFO", log_file=None):
"""日志装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 装饰逻辑
pass
return wrapper
return decorator
讲解:
- 三层嵌套:装饰器参数 → 装饰器 → 包装函数
- 使用
@wraps保留原函数信息 - 使用
*args, **kwargs保证通用性
📖 时间记录
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
start_time = time.time()
# ... 执行函数 ...
end_time = time.time()
exec_time = end_time - start_time
讲解:
datetime用于格式化时间戳time.time()用于计算执行时间- 分别记录开始和结束时间
📖 异常处理
try:
result = func(*args, **kwargs)
# 成功日志
return result
except Exception as e:
# 错误日志
raise # 重新抛出异常
讲解:
- 使用 try-except 捕获异常
- 记录异常信息后重新抛出,不改变原函数行为
- 确保异常情况下也能记录日志
📖 日志写入
def _write_logs(messages, log_file=None):
for message in messages:
print(message) # 控制台输出
if log_file:
with open(log_file, 'a', encoding='utf-8') as f:
f.write(message + '\n') # 文件输出
讲解:
- 支持同时输出到控制台和文件
- 使用
a模式追加写入,不覆盖原有内容 - 指定
utf-8编码,支持中文
3. 常见错误分析
❌ 错误 1:忘记使用 @wraps
# 错误写法
def log_decorator(level="INFO"):
def decorator(func):
def wrapper(*args, **kwargs): # ❌ 没有 @wraps
# ...
return wrapper
return decorator
# 问题:原函数名和文档丢失
print(add.__name__) # wrapper 而不是 add
正确写法:
@wraps(func)
def wrapper(*args, **kwargs):
# ...
❌ 错误 2:异常被吞掉
# 错误写法
try:
result = func(*args, **kwargs)
return result
except Exception as e:
print(f"错误:{e}")
# ❌ 没有重新抛出异常
# 问题:调用者无法知道函数失败了
正确写法:
except Exception as e:
print(f"错误:{e}")
raise # ✅ 重新抛出
❌ 错误 3:文件未关闭
# 错误写法
if log_file:
f = open(log_file, 'a')
f.write(message + '\n')
# ❌ 忘记关闭文件
# 问题:资源泄漏,可能导致数据丢失
正确写法:
if log_file:
with open(log_file, 'a', encoding='utf-8') as f:
f.write(message + '\n') # ✅ 自动关闭
❌ 错误 4:装饰器参数层级错误
# 错误写法
def log_decorator(func, level="INFO"): # ❌ func 位置错误
def wrapper(*args, **kwargs):
# ...
return wrapper
# 问题:无法作为 @log_decorator(level="INFO") 使用
正确写法:
def log_decorator(level="INFO"): # ✅ 先接收装饰器参数
def decorator(func): # 再接收函数
def wrapper(*args, **kwargs):
# ...
return wrapper
return decorator
第二部分:练习二答案 - 计时装饰器
4. 完整参考代码
# ============================================================
# 练习二:计时装饰器 - 完整参考答案
# ============================================================
import time
from functools import wraps
from collections import defaultdict
# ============================================================
# 方案 1:函数装饰器版本
# ============================================================
def timer_decorator(threshold=0.1, log_stats=False):
"""
计时装饰器
Args:
threshold: 阈值(秒),超过此时间发出警告
log_stats: 是否记录统计数据
Returns:
装饰器函数
"""
# 存储统计数据
stats = defaultdict(lambda: {
'count': 0,
'total_time': 0,
'min_time': float('inf'),
'max_time': 0,
'times': []
})
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 开始计时
start = time.perf_counter()
# 执行函数
result = func(*args, **kwargs)
# 结束计时
end = time.perf_counter()
exec_time = end - start
# 更新统计数据
if log_stats:
stats[func.__name__]['count'] += 1
stats[func.__name__]['total_time'] += exec_time
stats[func.__name__]['min_time'] = min(
stats[func.__name__]['min_time'], exec_time
)
stats[func.__name__]['max_time'] = max(
stats[func.__name__]['max_time'], exec_time
)
stats[func.__name__]['times'].append(exec_time)
# 输出时间信息
print(f"[{func.__name__}] 执行时间:{exec_time:.6f}秒")
# 阈值警告
if exec_time > threshold:
print(f"⚠️ 警告:{func.__name__} 执行时间超过阈值 {threshold}秒")
return result
# 添加统计信息访问方法
if log_stats:
def get_stats():
s = stats[func.__name__]
if s['count'] == 0:
return None
return {
'count': s['count'],
'avg_time': s['total_time'] / s['count'],
'min_time': s['min_time'],
'max_time': s['max_time'],
'total_time': s['total_time']
}
wrapper.get_stats = get_stats
return wrapper
return decorator
# ============================================================
# 方案 2:类装饰器版本
# ============================================================
class Timer:
"""计时类装饰器"""
def __init__(self, threshold=0.1, log_stats=False):
self.threshold = threshold
self.log_stats = log_stats
self.stats = {}
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
# 开始计时
start = time.perf_counter()
# 执行函数
result = func(*args, **kwargs)
# 结束计时
end = time.perf_counter()
exec_time = end - start
# 更新统计数据
if self.log_stats:
if func.__name__ not in self.stats:
self.stats[func.__name__] = {
'count': 0,
'total_time': 0,
'min_time': float('inf'),
'max_time': 0
}
self.stats[func.__name__]['count'] += 1
self.stats[func.__name__]['total_time'] += exec_time
self.stats[func.__name__]['min_time'] = min(
self.stats[func.__name__]['min_time'], exec_time
)
self.stats[func.__name__]['max_time'] = max(
self.stats[func.__name__]['max_time'], exec_time
)
# 输出时间信息
print(f"[{func.__name__}] 执行时间:{exec_time:.6f}秒")
# 阈值警告
if exec_time > self.threshold:
print(f"⚠️ 警告:{func.__name__} 执行时间超过阈值 {self.threshold}秒")
return result
# 添加统计信息访问方法
def get_stats(func_name=func.__name__):
if func_name not in self.stats:
return None
s = self.stats[func_name]
if s['count'] == 0:
return None
return {
'count': s['count'],
'avg_time': s['total_time'] / s['count'],
'min_time': s['min_time'],
'max_time': s['max_time'],
'total_time': s['total_time']
}
wrapper.get_stats = get_stats
wrapper.all_stats = lambda: self.stats
return wrapper
def print_all_stats(self):
"""打印所有函数的统计信息"""
print("\n" + "=" * 60)
print("所有函数性能统计")
print("=" * 60)
for func_name, stats in self.stats.items():
if stats['count'] > 0:
avg = stats['total_time'] / stats['count']
print(f"\n{func_name}:")
print(f" 调用次数:{stats['count']}")
print(f" 平均时间:{avg:.6f}秒")
print(f" 最短时间:{stats['min_time']:.6f}秒")
print(f" 最长时间:{stats['max_time']:.6f}秒")
print(f" 总时间:{stats['total_time']:.6f}秒")
# ============================================================
# 测试代码
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("=== 计时装饰器测试 ===")
print("=" * 60)
# 测试 1:基础计时
print("\n【测试 1】基础计时装饰器")
print("-" * 60)
@timer_decorator()
def quick_func():
time.sleep(0.01)
return "快速完成"
quick_func()
# 测试 2:阈值警告
print("\n【测试 2】阈值警告")
print("-" * 60)
@timer_decorator(threshold=0.05)
def slow_func():
time.sleep(0.1)
return "慢速完成"
slow_func()
# 测试 3:统计功能
print("\n【测试 3】统计功能")
print("-" * 60)
@timer_decorator(log_stats=True)
def repeat_func():
time.sleep(0.02)
return "重复执行"
for i in range(5):
repeat_func()
# 获取统计信息
stats = repeat_func.get_stats()
print(f"\n统计信息:{stats}")
# 测试 4:类装饰器
print("\n【测试 4】类装饰器版本")
print("-" * 60)
timer = Timer(threshold=0.05, log_stats=True)
@timer
def func1():
time.sleep(0.03)
return "func1"
@timer
def func2():
time.sleep(0.08)
return "func2"
func1()
func2()
func1()
func2()
# 打印所有统计
timer.print_all_stats()
print("\n" + "=" * 60)
print("=== 测试完成 ===")
print("=" * 60)
5. 逐行代码讲解
📖 精确计时
start = time.perf_counter()
# ... 执行函数 ...
end = time.perf_counter()
exec_time = end - start
讲解:
time.perf_counter()比time.time()更精确- 专门用于测量短时间间隔
- 不受系统时钟调整影响
📖 统计数据存储
stats = defaultdict(lambda: {
'count': 0,
'total_time': 0,
'min_time': float('inf'),
'max_time': 0,
'times': []
})
讲解:
- 使用
defaultdict自动初始化 - 存储调用次数、总时间、最小/最大时间
float('inf')确保第一次比较能更新最小值
📖 统计信息访问
def get_stats():
s = stats[func.__name__]
if s['count'] == 0:
return None
return {
'count': s['count'],
'avg_time': s['total_time'] / s['count'],
'min_time': s['min_time'],
'max_time': s['max_time'],
'total_time': s['total_time']
}
wrapper.get_stats = get_stats
讲解:
- 将统计方法绑定到 wrapper 函数
- 调用
func.get_stats()即可获取统计 - 计算平均值、最小值、最大值
📖 类装饰器实现
class Timer:
def __init__(self, threshold=0.1, log_stats=False):
self.threshold = threshold
self.log_stats = log_stats
self.stats = {}
def __call__(self, func):
# 返回包装函数
pass
def print_all_stats(self):
# 打印所有统计
pass
讲解:
- 使用类保存装饰器状态
__call__使实例可调用- 可以管理多个被装饰函数的统计
6. 常见错误分析
❌ 错误 1:使用 time.time() 而非 perf_counter()
# 不够精确
start = time.time()
end = time.time()
# 更精确
start = time.perf_counter() # ✅
end = time.perf_counter()
❌ 错误 2:统计数据作用域错误
# 错误写法
def timer_decorator():
stats = {} # ❌ 每个装饰器实例独立
def decorator(func):
def wrapper(*args, **kwargs):
# 无法跨函数统计
pass
return wrapper
return decorator
# 问题:无法统计多个函数的整体性能
正确写法:使用类装饰器或外部字典
❌ 错误 3:最小值初始化错误
# 错误写法
'min_time': 0 # ❌ 0 比任何正数都小
# 正确写法
'min_time': float('inf') # ✅ 无穷大
❌ 错误 4:忘记绑定统计方法
# 错误写法
def decorator(func):
def wrapper(*args, **kwargs):
# ...
return wrapper
# ❌ 没有绑定 get_stats
# 正确写法
wrapper.get_stats = get_stats # ✅
return wrapper
第三部分:练习三答案 - 缓存装饰器
7. 完整参考代码
# ============================================================
# 练习三:缓存装饰器 - 完整参考答案
# ============================================================
import time
from functools import wraps
from collections import OrderedDict
# ============================================================
# 方案 1:基础缓存装饰器
# ============================================================
def cache_decorator(max_size=128, ttl=None, key_func=None):
"""
缓存装饰器
Args:
max_size: 最大缓存条目数
ttl: 缓存有效期(秒),None 表示永不过期
key_func: 自定义缓存键生成函数
Returns:
装饰器函数
"""
# 使用 OrderedDict 实现 LRU
cache = OrderedDict()
# 存储过期时间
expire_times = {}
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存键
if key_func:
key = key_func(*args, **kwargs)
else:
key = (args, tuple(sorted(kwargs.items())))
# 检查缓存
current_time = time.time()
if key in cache:
# 检查是否过期
if ttl is None or current_time - expire_times[key] < ttl:
# 命中缓存,移到末尾(最近使用)
cache.move_to_end(key)
print(f"[CACHE HIT] {func.__name__}({key})")
return cache[key]
else:
# 过期,删除
del cache[key]
del expire_times[key]
# 未命中,执行函数
print(f"[CACHE MISS] {func.__name__}({key})")
result = func(*args, **kwargs)
# 存入缓存
# 检查大小限制
if len(cache) >= max_size:
# 删除最久未使用的
oldest_key = next(iter(cache))
del cache[oldest_key]
if oldest_key in expire_times:
del expire_times[oldest_key]
cache[key] = result
expire_times[key] = current_time
return result
# 添加缓存管理方法
def cache_info():
return {
'size': len(cache),
'max_size': max_size,
'keys': list(cache.keys())
}
def cache_clear():
cache.clear()
expire_times.clear()
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return wrapper
return decorator
# ============================================================
# 方案 2:使用 lru_cache 简化实现
# ============================================================
from functools import lru_cache
def simple_cache(max_size=128):
"""
简单缓存装饰器(使用 lru_cache)
"""
def decorator(func):
return lru_cache(maxsize=max_size)(func)
return decorator
# ============================================================
# 测试代码
# ============================================================
if __name__ == "__main__":
print("=" * 60)
print("=== 缓存装饰器测试 ===")
print("=" * 60)
# 测试 1:基础缓存
print("\n【测试 1】基础缓存装饰器")
print("-" * 60)
@cache_decorator()
def fibonacci(n):
"""计算斐波那契数列"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(f"fibonacci(10) = {fibonacci(10)}")
print(f"fibonacci(10) 再次调用 = {fibonacci(10)}")
print(f"缓存信息:{fibonacci.cache_info()}")
# 测试 2:缓存大小限制
print("\n【测试 2】缓存大小限制")
print("-" * 60)
@cache_decorator(max_size=3)
def compute(x):
time.sleep(0.01)
return x * 2
for i in range(5):
compute(i)
print(f"缓存信息:{compute.cache_info()}")
# 应该只保留最近的 3 个
# 测试 3:缓存过期
print("\n【测试 3】缓存过期")
print("-" * 60)
@cache_decorator(ttl=2)
def get_timestamp():
return time.time()
ts1 = get_timestamp()
print(f"第一次:{ts1}")
time.sleep(1)
ts2 = get_timestamp()
print(f"1 秒后:{ts2} (应该命中缓存)")
time.sleep(2)
ts3 = get_timestamp()
print(f"3 秒后:{ts3} (应该重新计算)")
# 测试 4:自定义缓存键
print("\n【测试 4】自定义缓存键")
print("-" * 60)
def make_key(*args, **kwargs):
# 只根据第一个参数生成键
return args[0] if args else None
@cache_decorator(key_func=make_key)
def process(data, option='default'):
return f"处理 {data} 使用 {option}"
print(process("data1", 'opt1'))
print(process("data1", 'opt2')) # 应该命中缓存
print(process("data2", 'opt1')) # 应该重新计算
# 测试 5:缓存清除
print("\n【测试 5】缓存清除")
print("-" * 60)
@cache_decorator()
def add(a, b):
return a + b
add(1, 2)
add(3, 4)
print(f"清除前:{add.cache_info()}")
add.cache_clear()
print(f"清除后:{add.cache_info()}")
print("\n" + "=" * 60)
print("=== 测试完成 ===")
print("=" * 60)
8. 逐行代码讲解
📖 LRU 缓存实现
cache = OrderedDict()
# 存入缓存
cache[key] = result
# 移到末尾(最近使用)
cache.move_to_end(key)
# 删除最久未使用的
oldest_key = next(iter(cache))
del cache[oldest_key]
讲解:
OrderedDict保持插入顺序move_to_end()将键移到末尾next(iter(cache))获取第一个键(最久未使用)
📖 缓存键生成
if key_func:
key = key_func(*args, **kwargs)
else:
key = (args, tuple(sorted(kwargs.items())))
讲解:
- 默认使用参数元组作为键
sorted(kwargs.items())确保关键字参数顺序一致- 自定义 key_func 可处理特殊情况
📖 缓存过期检查
current_time = time.time()
if key in cache:
if ttl is None or current_time - expire_times[key] < ttl:
# 未过期,返回缓存
return cache[key]
else:
# 过期,删除
del cache[key]
讲解:
- 存储每个键的过期时间
- 每次访问检查是否过期
- 过期后重新计算并更新缓存
📖 缓存管理方法
def cache_info():
return {
'size': len(cache),
'max_size': max_size,
'keys': list(cache.keys())
}
def cache_clear():
cache.clear()
expire_times.clear()
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
讲解:
- 提供缓存状态查询方法
- 提供手动清除缓存方法
- 绑定到 wrapper 函数上
9. 常见错误分析
❌ 错误 1:使用普通字典无法实现 LRU
# 错误写法
cache = {} # ❌ 普通字典无序(Python 3.7+ 有序但无 move_to_end)
# 正确写法
from collections import OrderedDict
cache = OrderedDict() # ✅
❌ 错误 2:缓存键不可哈希
# 错误写法
def wrapper(*args, **kwargs):
key = (args, kwargs) # ❌ kwargs 是字典,不可哈希
# 正确写法
key = (args, tuple(sorted(kwargs.items()))) # ✅
❌ 错误 3:过期时间检查错误
# 错误写法
if current_time - expire_times[key] > ttl: # ❌ 逻辑反了
return cache[key]
# 正确写法
if current_time - expire_times[key] < ttl: # ✅ 小于 ttl 才有效
return cache[key]
❌ 错误 4:缓存大小检查时机错误
# 错误写法
if len(cache) >= max_size:
del cache[oldest_key]
cache[key] = result # ❌ 先检查再存入,会多一条
# 正确写法
if len(cache) >= max_size:
del cache[oldest_key]
cache[key] = result # ✅ 或者先存入再检查
第四部分:优化建议与扩展
10. 代码优化建议
📌 日志装饰器优化
# 优化 1:添加日志级别过滤
def log_decorator(level="INFO", log_file=None):
min_level = LOG_LEVELS.get(level, 1)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if LOG_LEVELS.get(level, 1) < min_level:
return func(*args, **kwargs) # 跳过日志
# ...
# 优化 2:使用 logging 模块
import logging
def log_decorator(logger=None):
if logger is None:
logger = logging.getLogger(__name__)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logger.info(f"开始执行 {func.__name__}")
try:
result = func(*args, **kwargs)
logger.info(f"执行完成 {func.__name__}")
return result
except Exception as e:
logger.error(f"执行失败 {func.__name__}: {e}")
raise
return wrapper
return decorator
📌 计时装饰器优化
# 优化 1:添加线程安全
import threading
class Timer:
def __init__(self, threshold=0.1, log_stats=False):
self.threshold = threshold
self.log_stats = log_stats
self.stats = {}
self.lock = threading.Lock() # 线程锁
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
with self.lock: # 加锁
# 更新统计
pass
return result
return wrapper
# 优化 2:添加性能报告
def print_report(self):
import pandas as pd
df = pd.DataFrame(self.stats).T
df['avg_time'] = df['total_time'] / df['count']
print(df.sort_values('avg_time', ascending=False))
📌 缓存装饰器优化
# 优化 1:添加缓存命中率统计
def cache_decorator(max_size=128, ttl=None):
cache = OrderedDict()
stats = {'hits': 0, 'misses': 0}
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
key = (args, tuple(sorted(kwargs.items())))
if key in cache:
stats['hits'] += 1
# ...
else:
stats['misses'] += 1
# ...
def cache_stats():
total = stats['hits'] + stats['misses']
hit_rate = stats['hits'] / total if total > 0 else 0
return {
'hits': stats['hits'],
'misses': stats['misses'],
'hit_rate': f"{hit_rate:.2%}"
}
wrapper.cache_stats = cache_stats
return wrapper
return decorator
11. 扩展学习方向
┌─────────────────────────────────────────────────────────────┐
│ 扩展学习方向 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📚 高级装饰器模式 │
│ ├── 装饰器链(多个装饰器组合) │
│ ├── 装饰器工厂(动态生成装饰器) │
│ └── 装饰器注册表(集中管理装饰器) │
│ │
│ 📚 性能优化 │
│ ├── 异步装饰器(支持 async/await) │
│ ├── 装饰器性能分析 │
│ └── 装饰器开销优化 │
│ │
│ 📚 实际应用 │
│ ├── Web 框架装饰器(Flask/Django 路由) │
│ ├── API 装饰器(认证、限流、缓存) │
│ └── 测试装饰器(pytest fixtures) │
│ │
│ 📚 第三方库 │
│ ├── functools.lru_cache │
│ ├── cachetools(高级缓存) │
│ └── decorator 库(简化装饰器编写) │
│ │
└─────────────────────────────────────────────────────────────┘
📝 总结
✅ 本篇核心内容回顾
| 练习 | 核心知识点 | 难度 | 关键技能 |
|---|---|---|---|
| 练习一 | 日志装饰器 | ⭐⭐⭐ | 异常处理、文件操作 |
| 练习二 | 计时装饰器 | ⭐⭐⭐⭐ | 性能分析、统计计算 |
| 练习三 | 缓存装饰器 | ⭐⭐⭐⭐⭐ | LRU 算法、过期策略 |
💡 学习建议
┌─────────────────────────────────────────────────────────────┐
│ 学习建议 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ 理解装饰器执行流程 │
│ • 装饰器参数 → 装饰器 → 包装函数 │
│ • 从内到外装饰,从外到内执行 │
│ │
│ 2️⃣ 掌握闭包变量管理 │
│ • 理解变量作用域 │
│ • 使用 nonlocal 修改外部变量 │
│ • 注意可变对象的引用问题 │
│ │
│ 3️⃣ 始终使用 @wraps │
│ • 保留原函数信息 │
│ • 便于调试和文档生成 │
│ │
│ 4️⃣ 注意装饰器性能 │
│ • 装饰器本身有开销 │
│ • 避免在装饰器中做耗时操作 │
│ • 缓存装饰器要合理设置大小 │
│ │
└─────────────────────────────────────────────────────────────┘
🚀 下一步学习
完成本篇答案学习后,建议:
- 复习第十篇教程,巩固闭包和装饰器知识
- 独立完成所有练习,不要直接抄答案
- 尝试优化代码,添加更多功能
- 预习第十一篇:模块和包
- 在实际项目中使用装饰器,如日志、缓存、权限控制等
📅 更新时间:2026 年 3 月 27 日
✍️ 作者:书到用时方恨少! | 专注 Python 教学与AI技术分享
💌 如有疑问,欢迎在评论区留言讨论!
更多推荐



所有评论(0)