欢迎来到"Python教程从零基础到实战"系列的第二十一期!
学了这么多期,你现在已经能用 Python 写后端、做爬虫、搞数据分析、部署微服务了。但还有一个关键的瓶颈问题——**你的代码跑得快不快?**
想象一下这个场景:你写了一个爬取网页的程序,单次运行要花 30 分钟;或者你处理一份百万行数据表的脚本,跑了半小时才吐出一张报表。功能没问题,但用户体验很差。
这时候你就需要——**性能优化**。这一期我们从理论到实践,系统性地讲解 Python 性能优化的方方面面。
---
## 一、性能优化的第一步:测量,而不是猜
### 1.1 为什么要先测量?
一个经典的性能优化原则:**如果你不能测量它,你就不能优化它。** 很多人一上来就开始改代码——换个写法、拆个循环、加个缓存——但往往优化完之后发现性能根本没变化,因为真正的瓶颈根本不在你以为的地方。
这是"**过早优化是万恶之源**"的真正含义。不要猜,要去测量。
### 1.2 时间复杂度分析(大 O 表示法)
首先看理论层面——你的算法的复杂度是什么?
```python
# O(n) — 线性时间:遍历一次列表
deffind_target(items: list, target) -> int | None:
"""在列表中查找目标值"""
for i, item inenumerate(items):
if item == target:
return i
returnNone
# O(n^2) — 平方时间:双重嵌套循环
deffind_duplicates(items: list) -> list:
"""找出所有重复元素(低效写法)"""
duplicates = []
for i inrange(len(items)):
for j inrange(i + 1, len(items)):
if items[i] == items[j]:
duplicates.append(items[i])
return duplicates
# O(n log n) — 高效排序
defsort_items(items: list) -> list:
"""排序(Python 内置 Timsort 算法)"""
returnsorted(items)
# O(1) — 常数时间:字典/集合查找
defcontains_target(items_set: set, target) -> bool:
"""判断目标是否在集合中——O(1) 级别!"""
return target in items_set
```
**常见复杂度对比:**
| 复杂度 | 名称 | 100 条数据耗时估算 |
|--------|------|-------------------|
| O(1) | 常数 | <1 微秒 |
| O(log n) | 对数 | ~7 步 |
| O(n) | 线性 | ~100 步 |
| O(n log n) | 线性对数 | ~700 步 |
| O(n²) | 平方 | ~10,000 步 |
| O(2^n) | 指数 | ~1,267,650,600,228,229,401 |
对于 100 万条数据,O(n) 只需要 1 秒,O(n²) 可能需要约 11.5 天!
### 1.3 内置 timeit 模块:微基准测试
Python 标准库自带 `timeit`,适合快速测试一小段代码的执行时间:
```python
import timeit
# 测试列表推导 vs map 函数
list_comp_time = timeit.timeit(
stmt="[x ** 2 for x in range(1000)]",
number=10000,
)
map_time = timeit.timeit(
stmt="list(map(lambda x: x ** 2, range(1000)))",
number=10000,
)
print(f"列表推导式: {list_comp_time:.4f}s")
print(f"map 函数 : {map_time:.4f}s")
# 通常列表推导式更快,因为避免了 lambda 的函数调用开销
```
---
## 二、cProfile:找到真正的瓶颈
### 2.1 命令行 profiling
`cProfile` 是 Python 的内置性能剖析工具。它能告诉你程序中每个函数被调用了多少次、花了多少时间。
```bash
# 命令行直接分析脚本
python-mcProfile-scumulativemy_script.py
```
### 2.2 在代码中使用 cProfile
```python
import cProfile
import pstats
from io import StringIO
defslow_function_a():
"""模拟一个慢函数"""
total = 0
for i inrange(1_000_000):
total += i
return total
defmedium_function_b():
"""模拟中等速度函数"""
result = []
for i inrange(100_000):
result.append(i * 2)
return result
deffast_function_c():
"""模拟一个快函数"""
returnsum(range(10_000))
defmain():
slow_function_a()
medium_function_b()
fast_function_c()
if__name__ == "__main__":
# 开启性能分析
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
# 输出结果
stream = StringIO()
stats = pstats.Stats(profiler, stream=stream)
# 按总耗时排序
stats.sort_stats("cumulative")
print("=" * 60)
print("cProfile 分析报告")
print("=" * 60)
print(stream.getvalue())
# 只看前 10 行
stats.top_stats = stats.stats # 获取全部统计
for func_name, (calling_count, calling_times, total_time, cumulative_time) in \
sorted(stats.stats.items(), key=lambdax: x[1][3], reverse=True)[:10]:
print(f" {func_name}: 调用 {calling_count} 次, "
f"自身耗时 {calling_times:.4f}s, "
f"累计耗时 {cumulative_time:.4f}s")
```
输出类似:
```
============================================================
cProfile 分析报告
============================================================
7 function calls in 0.08400 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.062 0.062 0.084 0.084 <string>:1(<module>)
1 0.001 0.001 0.050 0.050 __main__:4(slow_function_a)
1 0.020 0.020 0.020 0.020 __main__:11(medium_function_b)
1 0.001 0.001 0.001 0.001 __main__:19(fast_function_c)
```
从报告立刻能看出:**slow_function_a 占了 50% 的累计耗时**,它就是头号嫌疑犯。
### 2.3 line_profiler:逐行分析
如果你的函数内部有几十上百行代码,想知道哪一行最慢,可以用第三方库 `line_profiler`:
```bash
pipinstallline_profiler
kernprof-lvmy_script.py
```
```python
from line_profiler import LineProfiler
@profile# ← kernprof 会自动识别这个装饰器
defprocess_data(data: list) -> list:
"""逐行分析这个函数"""
result = [] # 第 1 行
for item in data: # 第 2 行
temp = item * 2# 第 3 行
temp = temp ** 2# 第 4 行
result.append(temp) # 第 5 行
return result # 第 6 行
if__name__ == "__main__":
data = list(range(1_000_000))
process_data(data)
```
运行 `kernprof -lv script.py` 后,你会看到每一行的执行时间和耗时百分比,精确到行级别。
---
## 三、代码级别的优化技巧
### 3.1 用列表推导式替代 for 循环
```python
import time
numbers = list(range(100_000))
# ❌ 方式一:传统 for 循环
start = time.perf_counter()
result = []
for n in numbers:
result.append(n ** 2)
loop_time = time.perf_counter() - start
# ✅ 方式二:列表推导式(通常快 2-3 倍)
start = time.perf_counter()
result_comp = [n ** 2for n in numbers]
comp_time = time.perf_counter() - start
# ✅ 方式三:map + lambda(在某些场景下也快)
start = time.perf_counter()
result_map = list(map(lambdan: n ** 2, numbers))
map_time = time.perf_counter() - start
print(f"for 循环: {loop_time:.4f}s")
print(f"列表推导式: {comp_time:.4f}s (加速 {loop_time / comp_time:.2f}x)")
print(f"map 函数: {map_time:.4f}s (加速 {loop_time / map_time:.2f}x)")
```
典型输出:
```
for 循环: 0.0312s
列表推导式: 0.0145s (加速 2.15x)
map 函数: 0.0223s (加速 1.40x)
```
### 3.2 用集合替代列表进行成员检测
```python
import time
large_list = list(range(1_000_000))
large_set = set(large_list)
target = 999_999
# ❌ O(n) — 列表查找
start = time.perf_counter()
found_in_list = target in large_list
list_time = time.perf_counter() - start
# ✅ O(1) — 集合查找
start = time.perf_counter()
found_in_set = target in large_set
set_time = time.perf_counter() - start
print(f"列表查找: {list_time:.6f}s")
print(f"集合查找: {set_time:.6f}s (加速 {list_time / set_time:.0f}x)")
# 列表查找可能在毫秒级,集合查找在纳秒级!差了几千倍
```
### 3.3 字符串拼接的正确姿势
```python
import time
words = ["hello", "world", "python", "is", "awesome"]
# ❌ 方式一:直接用 + 循环拼接(每次创建新字符串对象)
start = time.perf_counter()
result = ""
for word in words:
result += word + " "
plus_time = time.perf_counter() - start
# ✅ 方式二:str.join()(只分配一次内存)
start = time.perf_counter()
result_join = " ".join(words)
join_time = time.perf_counter() - start
print(f"+ 拼接: {plus_time:.6f}s")
print(f"join: {join_time:.6f}s (加速 {plus_time / join_time:.0f}x)")
# 对于大量拼接,用 list 缓冲再 join 更是最佳实践:
long_texts = [f"Line {i}: some text data here."for i inrange(100_000)]
start = time.perf_counter()
big_string = "\n".join(long_texts)
join_big_time = time.perf_counter() - start
print(f"\n百万行文本 join: {join_big_time:.4f}s")
```
### 3.4 局部变量比全局变量快
```python
import time
GLOBAL_COUNTER = 0
defwith_global() -> int:
"""使用全局变量——每次都要查找命名空间"""
globalGLOBAL_COUNTER
for _ inrange(1_000_000):
GLOBAL_COUNTER += 1
returnGLOBAL_COUNTER
defwith_local(counter: int = 0) -> int:
"""使用局部变量——直接查表,快得多"""
for _ inrange(1_000_000):
counter += 1
return counter
# 对比
start = time.perf_counter()
with_global()
global_time = time.perf_counter() - start
start = time.perf_counter()
with_local()
local_time = time.perf_counter() - start
print(f"全局变量: {global_time:.4f}s")
print(f"局部变量: {local_time:.4f}s (加速 {global_time / local_time:.2f}x)")
# 局部变量通常快 1.5-2 倍
```
### 3.5 避免不必要的函数调用
```python
import time
defexpensive_computation(n: int) -> int:
"""假设这是一个耗时的计算"""
returnsum(i * i for i inrange(n))
defbad_approach(n: int) -> int:
"""每次都重新计算——浪费时间"""
a = expensive_computation(n)
b = expensive_computation(n) # ← 同样的计算做了两遍!
return a + b
defgood_approach(n: int) -> int:
"""用缓存避免重复计算"""
cache = {}
defmemoized_computation(x: int) -> int:
if x notin cache:
cache[x] = expensive_computation(x)
return cache[x]
return memoized_computation(n) + memoized_computation(n)
# 使用 functools.lru_cache —— 更优雅的方案
from functools import lru_cache
@lru_cache(maxsize=None)
defcached_expensive_computation(n: int) -> int:
"""自动缓存的函数"""
returnsum(i * i for i inrange(n))
```
---
## 四、NumPy 向量化运算:大数据量的杀手锏
当你对大型数组做运算时,循环再聪明也快不过 NumPy 的 C 底层向量化操作。
### 4.1 纯 Python 列表 vs NumPy 数组
```python
import time
import numpy as np
size = 1_000_000
# 生成数据
python_list = list(range(size))
numpy_array = np.arange(size)
# ===== 计算平方和 =====
# Python 列表(慢)
start = time.perf_counter()
sum_sq_py = sum(x ** 2for x in python_list)
py_time = time.perf_counter() - start
# NumPy 向量化(快!)
start = time.perf_counter()
sum_sq_np = np.sum(numpy_array ** 2)
np_time = time.perf_counter() - start
print(f"Python 列表: {py_time:.4f}s")
print(f"NumPy 数组 : {np_time:.4f}s")
print(f"加速比 : {py_time / np_time:.0f}x ⚡")
# 通常 NumPy 快 50-100 倍!
```
### 4.2 广播机制(Broadcasting)
```python
import numpy as np
# 创建一个 3x4 矩阵
matrix = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# ===== 对整个矩阵做运算,不需要任何循环 =====
squared = matrix ** 2# 每个元素平方
doubled = matrix * 2# 每个元素翻倍
normalized = (matrix - matrix.mean()) / matrix.std() # 标准化
print("原矩阵:\n", matrix)
print("\n平方后:\n", squared)
print("\n标准化后:\n", np.round(normalized, 2))
# ===== 广播:不同形状数组的自动对齐 =====
a = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
b = np.array([10, 20, 30]) # 1D 数组
# b 会沿着 a 的每一行广播
result = a + b
print("\n广播加法:\n", result)
# [[11, 22, 33],
# [14, 25, 36],
# [17, 28, 39]]
```
### 4.3 NumPy 实战:图片颜色转换
```python
import numpy as np
defconvert_to_grayscale(rgb_image: np.ndarray) -> np.ndarray:
"""
将 RGB 图片转换为灰度图
RGB_image 的形状为 (height, width, 3)
使用标准亮度权重:0.299R + 0.587G + 0.114B
"""
weights = np.array([0.299, 0.587, 0.114])
# 矩阵乘法:按权重加权求和
grayscale = np.dot(rgb_image.astype(np.float64), weights)
return grayscale.astype(np.uint8)
# 模拟一张 1000x1000 的彩色图片
height, width = 1000, 1000
random_image = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
start = time.perf_counter()
gray_image = convert_to_grayscale(random_image)
elapsed = time.perf_counter() - start
print(f"灰度转换完成!形状: {gray_image.shape}, 耗时: {elapsed:.4f}s")
# 纯 Python 循环实现同样的功能可能要几秒,NumPy 在毫秒级搞定
```
---
## 五、缓存与记忆化:空间换时间的艺术
### 5.1 @lru_cache 装饰器
```python
from functools import lru_cache
import time
# ===== 经典案例:斐波那契数列 =====
@lru_cache(maxsize=None)
deffibonacci(n: int) -> int:
"""带缓存的递归斐波那契——O(n) 而非 O(2^n)!"""
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# 没有缓存的版本(极慢)
deffibonacci_no_cache(n: int) -> int:
if n < 2:
return n
return fibonacci_no_cache(n - 1) + fibonacci_no_cache(n - 2)
# 对比
start = time.perf_counter()
result_cached = fibonacci(50)
cached_time = time.perf_counter() - start
start = time.perf_counter()
result_uncached = fibonacci_no_cache(40) # 不敢算 50,太慢了
uncached_time = time.perf_counter() - start
print(f"带缓存 fib(50) = {result_cached}, 耗时: {cached_time:.6f}s")
print(f"无缓存 fib(40) = {result_uncached}, 耗时: {uncached_time:.4f}s")
print(f"加速比: 快到无法用数字形容(缓存版本 50 位计算只需微秒)")
```
### 5.2 自定义缓存类
```python
import time
import hashlib
from typing import Any, Callable, Optional
classCache:
"""通用的函数缓存装饰器"""
def__init__(self, max_size: int = 128, ttl_seconds: int = 3600):
self.max_size = max_size
self.ttl_seconds = ttl_seconds
self._cache: dict[str, tuple[Any, float]] = {}
def__call__(self, func: Callable) -> Callable:
import functools
@functools.wraps(func)
defwrapper(*args, **kwargs):
# 生成缓存键
key_data = f"{func.__name__}:{args}:{sorted(kwargs.items())}"
key = hashlib.md5(key_data.encode()).hexdigest()
# 检查缓存是否存在且未过期
if key inself._cache:
value, expire_at = self._cache[key]
if time.time() < expire_at:
return value
# 计算并缓存
result = func(*args, **kwargs)
expire_at = time.time() + self.ttl_seconds
self._cache[key] = (result, expire_at)
# 缓存满时移除最旧的
iflen(self._cache) > self.max_size:
oldest_key = min(self._cache, key=lambdak: self._cache[k][1])
delself._cache[oldest_key]
return result
return wrapper
@Cache(max_size=256, ttl_seconds=1800)
deffetch_data_from_api(user_id: int) -> dict:
"""模拟从 API 获取用户数据"""
time.sleep(1) # 模拟网络延迟
return {"user_id": user_id, "name": f"User_{user_id}", "score": user_id * 17}
# 第一次调用:1 秒
start = time.perf_counter()
data1 = fetch_data_from_api(42)
t1 = time.perf_counter() - start
print(f"第一次调用: {data1}, 耗时: {t1:.2f}s(有网络延迟)")
# 第二次调用同一个参数:瞬间返回
start = time.perf_counter()
data2 = fetch_data_from_api(42)
t2 = time.perf_counter() - start
print(f"第二次调用: {data2}, 耗时: {t2:.4f}s(命中缓存)")
```
---
## 六、生成器和惰性求值:减少内存占用
有时候性能瓶颈不是 CPU 而是**内存**。生成器能在需要时才计算下一个值,而不是一次性把全部数据加载进内存。
### 6.1 生成器表达式 vs 列表推导式
```python
import sys
# 生成 1 亿个数的平方
gen_expr = (x ** 2for x inrange(100_000_000))
list_comp = [x ** 2for x inrange(100_000_000)]
# 对比内存占用
print(f"生成器表达式: {sys.getsizeof(gen_expr)} bytes")
print(f"列表推导式 : {sys.getsizeof(list_comp)} bytes")
# 生成器: 112 bytes(几乎不占内存)
# 列表 : 约 800 MB!(全部数据都在内存里)
```
### 6.2 惰性读取大文件
```python
defread_large_file(filepath: str) -> list[str]:
"""逐行读取——不一次性把整个文件加载到内存"""
lines = []
withopen(filepath, "r", encoding="utf-8") as f:
for line in f: # ← 这里!for 循环自动惰性读取
lines.append(line.strip())
return lines
defcount_words_generator(filepaths: list[str]) -> int:
"""惰性统计多个文件的总词数"""
total = 0
for filepath in filepaths:
withopen(filepath, "r", encoding="utf-8") as f:
for line in f: # ← 逐行处理,不加载全文
total += len(line.split())
return total
```
---
## 七、实操练习
### 练习题
**第 1 题:为一个慢函数添加缓存**
下面的函数用于计算"汉明距离"(两个整数的二进制表示中有多少位不同)。请分别用普通方法和 `@lru_cache` 来写,并测试性能差异:
```python
# 基础版(无缓存)
defhamming_distance(a: int, b: int) -> int:
returnbin(a ^ b).count('1')
# 参考答案:加上 @lru_cache 后再测试大规模调用时的加速效果
from functools import lru_cache
@lru_cache(maxsize=None)
defhamming_distance_cached(a: int, b: int) -> int:
returnbin(a ^ b).count('1')
# 大量重复调用时,缓存版的加速效果明显
```
**第 2 题:用 NumPy 替换纯 Python 循环**
写一段代码对比以下两种方法的性能:
- 方法 A:用纯 Python 循环计算两个长度为 100 万的数组的点积
- 方法 B:用 NumPy 计算点积
```python
import time
import numpy as np
size = 1_000_000
a_list = list(range(size))
b_list = list(range(1, size + 1))
# 方法 A
start = time.perf_counter()
dot_product_py = sum(x * y for x, y inzip(a_list, b_list))
py_time = time.perf_counter() - start
# 方法 B
a_np = np.array(a_list)
b_np = np.array(b_list)
start = time.perf_counter()
dot_product_np = np.dot(a_np, b_np)
np_time = time.perf_counter() - start
print(f"Python: {py_time:.4f}s")
print(f"NumPy : {np_time:.4f}s")
print(f"加速: {py_time / np_time:.0f}x")
```
**第 3 题:编写性能基准测试套件**
为你的一个项目写一个简单的性能基准测试,包含三个指标:
- 最小耗时
- 平均耗时
- 最大耗时
至少跑 10 次取统计值。可以用 Python 标准库完成:
```python
import time
import statistics
defbenchmark(func, *args, rounds: int = 20) -> dict:
"""运行基准测试"""
times = []
for _ inrange(rounds):
start = time.perf_counter()
func(*args)
elapsed = time.perf_counter() - start
times.append(elapsed)
return {
"min_ms": round(min(times) * 1000, 4),
"avg_ms": round(statistics.mean(times) * 1000, 4),
"max_ms": round(max(times) * 1000, 4),
"stdev_ms": round(statistics.stdev(times) * 1000, 4) if rounds > 1else0,
}
# 测试函数
import random
sample_data = [random.randint(0, 10000) for _ inrange(100_000)]
results = benchmark(sorted, sample_data, rounds=20)
print(f"排序 10 万个整数:")
for metric, value in results.items():
print(f" {metric}: {value}ms")
```
**第 4 题:对比不同数据结构同一操作的耗时**
研究 Python 中 `list`、`set`、`dict`、`tuple` 各自的操作复杂度,通过实验验证它们:
```python
import timeit
setup = """
my_list = list(range(100_000))
my_set = set(my_list)
my_dict = {x: x for x in range(100_000)}
my_tuple = tuple(my_list)
target = 99_999
"""
# 各项操作耗时
ops = {
"list 成员检测 (in)": f"'{target}' in my_list",
"set 成员检测 (in)": f"'{target}' in my_set",
"dict 成员检测 (in)": f"'{target}' in my_dict",
"list 索引访问": "my_list[50000]",
"tuple 索引访问": "my_tuple[50000]",
}
print(f"{'操作':<25}{'耗时(秒)':<12}")
print("-" * 40)
for name, stmt in ops.items():
t = timeit.timeit(stmt, setup=setup, number=10000)
print(f" {name:<22}{t:.6f}")
```
预期结果应该让你看到:set/dict 的 O(1) 查找远比 list 的 O(n) 查找快。
**第 5 题:完整性能优化挑战**
给定一份 100 万行的日志数据(模拟),请完成以下任务:
1. 用 cProfile 找到处理日志的瓶颈
2. 至少提出并实现三种优化方案
3. 每种方案都要对比优化前后的性能
4. 写出一份简洁的性能优化报告
提示:可以使用以下模拟数据来生成测试文件:
```python
import random
log_templates = [
"GET /api/users/{} HTTP/1.1 200 {}ms",
"POST /api/orders HTTP/1.1 201 {}ms",
"GET /api/products?page={} HTTP/1.1 200 {}ms",
"DELETE /api/cache/invalid HTTP/1.1 204 {}ms",
]
withopen("access.log", "w") as f:
for _ inrange(1_000_000):
template = random.choice(log_templates)
line = template.format(
random.randint(1, 10000),
random.randint(10, 5000),
)
f.write(line + "\n")
```
---
## 八、总结
这一期我们深入讲解了 Python 性能优化的核心方法论和实战技巧。回顾一下要点:
1.**测量先行**——用 `timeit`、`cProfile`、`line_profiler` 找到真正的瓶颈。不要在盲盒式地猜哪里慢了。
2.**数据结构选择**——`set` 和 `dict` 的查找是 O(1),`list` 是 O(n)。选对工具,性能翻倍。
3.**NumPy 向量化**——在处理数值数据时,NumPy 能把速度提升 50-100 倍。这是 Python 数据科学的核心武器。
4.**缓存和记忆化**——用 `@lru_cache` 避免重复计算,斐波那契数列从数分钟变到微秒就是经典案例。
5.**生成器惰性求值**——处理海量数据时,别一次性加载全部内存。按需生产,省内存又提速。
6.**代码习惯**——列表推导式 > for 循环、`join()` > `+` 拼接、局部变量 > 全局变量。这些看起来小小的改动,累积起来效果惊人。
最重要的一句话:**在优化之前先让代码工作(Make it work),然后让它正确(Make it right),最后才让它快(Make it fast)。**
---
## 九、下期预告
**Episode 22:Python 设计模式实战——让代码更优雅、更可维护**
我们已经掌握了性能优化的方法,接下来聊聊软件的"结构设计"。设计模式是编程领域最经典的话题之一:
- 单例模式、工厂模式、策略模式等常用模式的 Pythonic 实现
- 如何用设计模式解决实际问题(配置管理、HTTP 客户端封装、插件系统)
- 代码重构实战:把一个"面条代码"项目改造成优雅的可扩展架构
- SOLID 五大原则在 Python 中的具体体现
敬请期待!
---
**📚 系列回顾**
到目前为止我们学过的内容:
| Episode | 主题 |
|---------|------|
| 01 | 环境搭建与基础语法 |
| 02 | 数据结构与字符串处理 |
| 03 | 面向对象编程 |
| 04 | 装饰器、生成器与文件 IO |
| 05 | 爬虫入门 |
| 06 | 数据分析入门 |
| 07 | Web 后端开发 |
| 08 | AI 实战入门 |
| 09 | 前端入门 |
| 10 | 项目实战——Dashboard |
| 11 | Docker 容器化部署 |
| 12 | 数据库进阶 |
| 13 | 异步编程与并发 |
| 14 | 日志系统与调试技巧 |
| 15 | 自动化运维与脚本实战 |
| 16 | 单元测试与代码质量 |
| 17 | 微服务入门 |
| 18 | CI/CD 与 DevOps 实战 |
| 19 | 并发与多线程深度实践 |
| 20 | 包管理与发布 |
| 21 | 性能优化全指南 ← **本期** |
恭喜你!从零基础到现在,你已经掌握了一套完整的 Python 工程技能链——能写代码、能优化、能部署、能测试。接下来就看你想拿这套技能去做什么了!🚀