性能优化与调试
找到瓶颈再优化——profiling 定位热点,内存分析找泄漏,实用调试技巧解决疑难 Bug。
性能优化流程
graph LR
MEASURE[测量基准] --> PROFILE[找到瓶颈]
PROFILE --> OPTIMIZE[定向优化]
OPTIMIZE --> VERIFY[验证效果]
VERIFY --> MEASURE
PROFILE --> P1[cProfile CPU]
PROFILE --> P2[memory_profiler 内存]
PROFILE --> P3[line_profiler 逐行]
OPTIMIZE --> O1[算法优化]
OPTIMIZE --> O2[缓存]
OPTIMIZE --> O3[并发]
OPTIMIZE --> O4[C 扩展]
style MEASURE fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style PROFILE fill:#fff3e0,stroke:#f57c00,stroke-width:2px
性能测量
"""
性能测量:先量后优
"""
import time
from functools import wraps
# === 计时装饰器 ===
def timeit(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__}: {elapsed:.4f}s")
return result
return wrapper
@timeit
def slow_function():
total = 0
for i in range(1_000_000):
total += i ** 2
return total
slow_function()
# === cProfile 详细分析 ===
import cProfile
import pstats
from io import StringIO
def profile_function(func, *args, **kwargs):
"""函数级性能分析"""
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
stream = StringIO()
stats = pstats.Stats(profiler, stream=stream)
stats.sort_stats("cumulative")
stats.print_stats(10) # Top 10
print(stream.getvalue())
return result
# 命令行使用
# python -m cProfile -s cumulative myscript.py
常见优化技巧
"""
Python 性能优化实战
"""
from functools import lru_cache
import timeit
# === 1. 选对数据结构 ===
# 查找:set/dict O(1) vs list O(n)
big_list = list(range(100_000))
big_set = set(range(100_000))
# list 查找
t1 = timeit.timeit(lambda: 99_999 in big_list, number=1000)
# set 查找
t2 = timeit.timeit(lambda: 99_999 in big_set, number=1000)
print(f"list 查找: {t1:.4f}s, set 查找: {t2:.6f}s")
# set 快 1000x+
# === 2. 字符串拼接 ===
# ❌ 慢:循环 += 是 O(n²)
def concat_slow(n):
s = ""
for i in range(n):
s += str(i)
return s
# ✅ 快:join 是 O(n)
def concat_fast(n):
return "".join(str(i) for i in range(n))
# === 3. 缓存 ===
@lru_cache(maxsize=512)
def fib(n: int) -> int:
"""缓存递归"""
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(100)) # 瞬间完成
print(fib.cache_info())
# === 4. 局部变量比全局快 ===
def sum_with_local():
"""使用局部变量"""
total = 0
_range = range # 缓存内置函数到局部
for i in _range(1_000_000):
total += i
return total
# === 5. 列表推导比循环快 ===
# ❌
result = []
for i in range(10000):
result.append(i ** 2)
# ✅ 快 20-30%
result = [i ** 2 for i in range(10000)]
内存优化
"""
内存分析与优化
"""
import sys
from dataclasses import dataclass
# === 查看对象大小 ===
data = {
"list[1000]": sys.getsizeof(list(range(1000))),
"tuple[1000]": sys.getsizeof(tuple(range(1000))),
"set[1000]": sys.getsizeof(set(range(1000))),
"dict[1000]": sys.getsizeof({i: i for i in range(1000)}),
}
for name, size in data.items():
print(f" {name}: {size:,} bytes")
# === 生成器省内存 ===
# ❌ 一次性加载
all_data = [process(x) for x in range(1_000_000)] # 占大量内存
# ✅ 惰性处理
def process(x):
return x ** 2
gen_data = (process(x) for x in range(1_000_000)) # 几乎不占内存
# === __slots__ 省实例内存 ===
class HeavyObject:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
class LightObject:
__slots__ = ("x", "y", "z")
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# 创建 10 万个实例,__slots__ 省 40%+ 内存
调试技巧
"""
Python 调试实用技巧
"""
# === 1. breakpoint()(推荐)===
def buggy_function(data):
result = []
for item in data:
processed = item * 2
breakpoint() # 进入 pdb 调试器
result.append(processed)
return result
# pdb 常用命令:
# n(ext) - 下一行
# s(tep) - 进入函数
# c(ontinue)- 继续执行
# p expr - 打印表达式
# l(ist) - 显示代码
# q(uit) - 退出
# === 2. 丰富的 print 调试 ===
def debug_print(*args, **kwargs):
"""带上下文的调试打印"""
import inspect
frame = inspect.currentframe().f_back
filename = frame.f_code.co_filename.split("/")[-1]
lineno = frame.f_lineno
func_name = frame.f_code.co_name
print(f"[{filename}:{lineno} {func_name}]", *args, **kwargs)
# === 3. 异常链追踪 ===
class AppError(Exception):
pass
def risky_operation():
try:
result = 1 / 0
except ZeroDivisionError as e:
raise AppError("计算失败") from e # 保留原始异常链
# === 4. logging 替代 print ===
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
log = logging.getLogger(__name__)
log.debug("调试细节")
log.info("正常信息")
log.warning("警告")
log.error("错误")
优化检查清单
| 阶段 | 检查项 | 工具 |
|---|---|---|
| 测量 | 定位 CPU 热点函数 | cProfile |
| 测量 | 逐行耗时分析 | line_profiler |
| 测量 | 内存使用峰值 | memory_profiler |
| 优化 | 算法复杂度是否合理 | 手动分析 |
| 优化 | 热点函数是否可缓存 | lru_cache |
| 优化 | 数据结构选择是否正确 | set/dict 替代 list |
| 优化 | 是否可以并发/异步 | asyncio / threading |
| 验证 | 优化后性能提升多少 | timeit / benchmark |
本章小结
| 知识点 | 要点 |
|---|---|
| 测量优先 | cProfile + timeit 定位瓶颈 |
| 数据结构 | set/dict 查找 O(1) |
| 缓存 | lru_cache 避免重计算 |
| 内存 | 生成器 + slots |
| 调试 | breakpoint() + logging |
下一章:实战项目——数据管道与 API 服务。