🚀 Python 性能分析工具实战:cProfile、memory_profiler、line_profiler 使用指南

🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

- [🚀 Python 性能分析工具实战:cProfile、memory_profiler、line_profiler 使用指南](#🚀 Python 性能分析工具实战:cProfile、memory_profiler、line_profiler 使用指南)
-
- 摘要
- 内容
- 一、为什么要做性能分析?
-
- [1.1 「我觉得这里慢」为什么不可靠?](#1.1 「我觉得这里慢」为什么不可靠?)
- [1.2 性能分析三角模型](#1.2 性能分析三角模型)
- [二、⏱️ cProfile:CPU 性能分析利器](#二、⏱️ cProfile:CPU 性能分析利器)
- [三、📏 line_profiler:逐行性能分析](#三、📏 line_profiler:逐行性能分析)
-
- [3.1 安装与基本使用](#3.1 安装与基本使用)
- [3.2 解读逐行结果](#3.2 解读逐行结果)
- [3.3 在 Jupyter Notebook 中使用](#3.3 在 Jupyter Notebook 中使用)
- [3.4 高级:排除无关代码](#3.4 高级:排除无关代码)
- [四、🧠 memory_profiler:内存使用分析](#四、🧠 memory_profiler:内存使用分析)
-
- [4.1 安装与基本使用](#4.1 安装与基本使用)
- [4.2 解读输出结果](#4.2 解读输出结果)
- [4.3 内存随时间的变化(mprof)](#4.3 内存随时间的变化(mprof))
- [4.4 使用 tracemalloc 精确追踪对象](#4.4 使用 tracemalloc 精确追踪对象)
- [4.5 实战:排查内存泄漏](#4.5 实战:排查内存泄漏)
- [五、📊 py-spy:无侵入式分析](#五、📊 py-spy:无侵入式分析)
-
- [5.1 适用场景](#5.1 适用场景)
- [5.2 安装与使用](#5.2 安装与使用)
- [5.3 py-spy vs cProfile 对比](#5.3 py-spy vs cProfile 对比)
- [六、🔬 实战案例:完整的性能优化流程](#六、🔬 实战案例:完整的性能优化流程)
-
- 场景:日志分析系统
- [Step 1:cProfile 宏观定位](#Step 1:cProfile 宏观定位)
- [Step 2:line_profiler 微观分析](#Step 2:line_profiler 微观分析)
- [Step 3:针对性优化](#Step 3:针对性优化)
- [Step 4:memory_profiler 排查内存](#Step 4:memory_profiler 排查内存)
- [Step 5:优化结果对比](#Step 5:优化结果对比)
- [七、🧰 工具选择决策树](#七、🧰 工具选择决策树)
- [八、⚡ 性能优化通用原则](#八、⚡ 性能优化通用原则)
-
- [8.1 优先选择](#8.1 优先选择)
- [8.2 局部引用加速](#8.2 局部引用加速)
- [8.3 善用内置数据结构](#8.3 善用内置数据结构)
- 九、常见陷阱与注意事项
- 十、总结
- 结尾
摘要
人类对程序性能的直觉判断往往不可靠------90% 的执行时间通常花在 10% 的代码上。性能分析的核心理念是"不要猜,要测"。本文系统介绍 Python 性能分析的方法论与核心工具链,涵盖 cProfile 函数级 CPU 分析、SnakeViz 可视化、line_profiler 逐行分析、memory_profiler 内存追踪、tracemalloc 对象分配定位、py-spy 生产环境零侵入采样等工具,通过完整的日志分析系统优化案例展示从宏观定位到微观优化再到结果验证的完整工作流,帮助你写出更高效的 Python 代码。
🚀 个人主页 : 有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
内容
一、为什么要做性能分析?
1.1 「我觉得这里慢」为什么不可靠?
人类对程序性能的直觉判断往往是不准确的。研究数据表明:
- 开发者通常高估 了 I/O 操作的开销,而低估了内存分配和函数调用的成本
- 一段代码中,90% 的执行时间通常花在 10% 的代码上(二八法则的计算机版本)
- 直觉式的优化常常优化了不重要的路径,浪费了开发时间
性能分析的核心理念:不要猜,要测。
1.2 性能分析三角模型
| 维度 | 覆盖工具 | 典型问题 |
|---|---|---|
| CPU 时间 | cProfile、py-spy | 哪个函数占用了最多的计算资源? |
| 内存占用 | memory_profiler、tracemalloc | 内存泄漏在哪里?对象为何没被回收? |
| 逐行开销 | line_profiler | 函数内哪一行代码最慢? |
| I/O 等待 | asyncio 调试、py-spy | 程序在等什么?网络还是磁盘? |
🔑 成熟的做法 :先用 cProfile 做宏观定位,再用 line_profiler 做微观分析,最后用 memory_profiler 排查内存问题------从粗到细,逐步聚焦。
二、⏱️ cProfile:CPU 性能分析利器
cProfile 是 Python 标准库自带的确定性性能分析器(deterministic profiler),零依赖,开箱即用。
2.1 基础用法
方式一:命令行运行
bash
python -m cProfile -o output.prof my_script.py
参数说明:
-o output.prof:将分析结果保存到文件(推荐,避免终端刷屏)-s cumulative:按累计时间排序打印
方式二:代码中嵌入
python
import cProfile
import pstats
def main():
# 业务逻辑
...
if __name__ == "__main__":
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative") # 按累计耗时排序
stats.print_stats(20) # 只显示前 20 条
方式三:使用 Context Manager(Python 3.8+)
python
import cProfile
import pstats
from contextlib import contextmanager
@contextmanager
def profiled():
profiler = cProfile.Profile()
profiler.enable()
yield
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats(30)
# 使用
with profiled():
result = expensive_operation()
2.2 解读输出结果
text
1000005 function calls in 2.345 seconds
Ordered by: cumulative time
List reduced from 50 to 20 due to restriction <20>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 2.345 2.345 main.py:1(<module>)
1 0.001 0.001 2.344 2.344 main.py:10(data_pipeline)
10000 0.500 0.000 1.800 0.000 main.py:25(process_item)
10000 0.300 0.000 0.800 0.000 main.py:40(validate)
30000 0.400 0.000 0.400 0.000 {built-in method builtins.len}
10000 0.200 0.000 0.200 0.000 main.py:55(transform)
50000 0.100 0.000 0.100 0.000 {method 'append' of 'list' objects}
关键字段解读:
| 字段 | 含义 | 判断标准 |
|---|---|---|
ncalls |
调用次数 | 过高可能是设计问题(如循环内重复调用) |
tottime |
函数自身耗时(不含子调用) | 纯计算密集度的指标 |
cumtime |
累计耗时(含子调用) | 包含整条调用链的总开销 |
percall (cumtime) |
平均每次调用耗时 | cumtime / ncalls,识别单次操作的开销 |
💡 快速判断经验:
tottime高 +ncalls低 → 函数本身计算密集,需优化算法tottime低 +ncalls极高 → 调用次数过多,需减少调用频率(如缓存)cumtime远大于tottime→ 函数调用了昂贵的子函数
2.3 使用 SnakeViz 可视化分析
文本输出可读性有限,SnakeViz 提供交互式可视化:
bash
pip install snakeviz
snakeviz output.prof
这会在浏览器中打开一个火焰图风格的交互式面板,支持:
- 鼠标悬停查看函数详情
- 点击展开/折叠调用栈
- 按名称搜索特定函数
- 切换视图(Icicle / Sunburst)
python
# 等价的一步到位
import cProfile
import subprocess
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()
profiler.dump_stats("output.prof")
subprocess.run(["snakeviz", "output.prof"]) # 自动打开浏览器
2.4 只分析目标代码段
常规的 cProfile.run() 会分析整个程序,产生大量噪音。精确打点更高效:
python
import cProfile
# 只分析 min 和 max 方法
def analyze_sorting(data):
with cProfile.Profile() as profiler:
profiler.enable()
result = sorted(data, key=lambda x: x["price"])
profiler.disable()
profiler.dump_stats("sort_only.prof")
# 另一种写法:使用 runctx
cProfile.runctx("sorted(data, key=lambda x: x['price'])",
globals(), locals(), "sort_only_2.prof")
2.5 对比优化前后
python
import cProfile
import pstats
import io
def profile_function(func, *args, **kwargs):
"""封装一个通用的性能分析函数"""
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats("cumtime")
stats.print_stats(15)
return result, s.getvalue()
# 使用
result_before, report_before = profile_function(old_version, data)
result_after, report_after = profile_function(new_version, data)
三、📏 line_profiler:逐行性能分析
cProfile 只能看到函数级别 的耗时。当你定位到某个函数是瓶颈后,line_profiler 能告诉你函数内的哪一行代码最慢。
3.1 安装与基本使用
bash
pip install line_profiler
python
# 在目标函数上添加 @profile 装饰器(无需 import,由 kernprof 注入)
@profile
def process_data(items):
result = []
for item in items:
# 中间处理
temp = item["value"] * 2
temp = temp ** 0.5
result.append(temp)
return result
if __name__ == "__main__":
data = [{"value": i} for i in range(100000)]
process_data(data)
bash
kernprof -l -v my_script.py
-l:逐行分析模式(line-by-line)-v:分析完成后立即输出结果
3.2 解读逐行结果
text
Timer unit: 1e-06 s
Total time: 0.523456 s
File: my_script.py
Function: process_data at line 5
Line # Hits Time Per Hit % Time Line Contents
==============================================================
5 @profile
6 def process_data(items):
7 1 3.0 3.0 0.0% result = []
8 100001 15000.0 0.2 2.9% for item in items:
9 100000 120000.0 1.2 22.9% temp = item["value"] * 2
10 100000 280000.0 2.8 53.5% temp = temp ** 0.5
11 100000 108000.0 1.1 20.6% result.append(temp)
12 1 0.5 0.5 0.0% return result
关键解读:
- 第 10 行 占用了 53.5% 的时间------
** 0.5(开平方)远比乘法和索引访问昂贵 - 优化方向:将
** 0.5替换为math.sqrt()
python
import math
@profile
def process_data_optimized(items):
result = []
sqrt = math.sqrt # 局部引用加速
for item in items:
temp = item["value"] * 2
temp = sqrt(temp) # 使用 math.sqrt 替代 ** 0.5
result.append(temp)
return result
3.3 在 Jupyter Notebook 中使用
python
%load_ext line_profiler
def slow_function(n):
total = 0
for i in range(n):
total += i ** 2
return total
# 使用 %lprun 魔法命令
%lprun -f slow_function slow_function(100000)
3.4 高级:排除无关代码
当函数调用了其他函数时,我们可以使用 -f 同时分析多个函数:
bash
kernprof -l -v -f process_data -f validate_data my_script.py
或者在 IPython 中:
python
%lprun -f process_data -f validate_data process_data(items)
四、🧠 memory_profiler:内存使用分析
内存泄漏是 Python 长服务中最常见的稳定性隐患。memory_profiler 可以精确追踪每行代码的内存变化。
4.1 安装与基本使用
bash
pip install memory_profiler psutil # psutil 可大幅提升性能
同样使用 @profile 装饰器(注意:与 line_profiler 的装饰器同名但不同来源):
python
from memory_profiler import profile
@profile
def create_large_structure():
# 创建一个占用内存的数据结构
large_list = [i for i in range(1_000_000)]
large_dict = {str(i): i for i in range(500_000)}
# 嵌套结构
nested = [[j for j in range(100)] for _ in range(10_000)]
return large_list, large_dict, nested
if __name__ == "__main__":
result = create_large_structure()
bash
python -m memory_profiler my_script.py
4.2 解读输出结果
text
Line # Mem usage Increment Occurences Line Contents
============================================================
5 45.2 MiB 45.2 MiB 1 @profile
6 def create_large_structure():
7 123.4 MiB 78.2 MiB 1 large_list = [i for i in range(1_000_000)]
8 162.1 MiB 38.7 MiB 1 large_dict = {str(i): i for i in range(500_000)}
9 170.5 MiB 8.4 MiB 1 nested = [[j for j in range(100)] for _ in range(10_000)]
10 170.5 MiB 0.0 MiB 1 return large_list, large_dict, nested
注意:
Mem usage是该行执行后的总内存Increment是该行导致的内存增量(但有 GC 干扰时可能不准)- 逐行内存分析的精度受垃圾回收器影响 ,建议结合
tracemalloc做精确追踪
4.3 内存随时间的变化(mprof)
比起单次快照,更常见的是需要了解程序运行过程中的内存变化趋势:
bash
mprof run my_script.py
mprof plot # 生成内存随时间变化的曲线图
生成的效果图会包含以下信息:
- X 轴:时间(秒)
- Y 轴:内存占用(MiB)
- 可以叠加多条曲线对比优化前后的效果
代码中标记时间点:
python
from memory_profiler import memory_usage
import time
def track_memory():
mem_usage = memory_usage(
(process_func, (arg1,), {"kwarg": value}),
interval=0.1, # 每 0.1 秒采样一次
timeout=30, # 最多采集 30 秒
max_iterations=100 # 最多采样 100 次
)
print(f"峰值内存: {max(mem_usage):.2f} MiB")
print(f"平均内存: {sum(mem_usage)/len(mem_usage):.2f} MiB")
4.4 使用 tracemalloc 精确追踪对象
Python 3.4+ 内置的 tracemalloc 模块可以追踪每个对象的分配位置:
python
import tracemalloc
def trace_allocation():
tracemalloc.start()
# 运行你的代码
data = load_large_dataset()
snapshot = tracemalloc.take_snapshot()
stats = snapshot.statistics("lineno") # 按行号分组
print("=== 内存分配 Top 10 ===")
for stat in stats[:10]:
print(stat)
# 对比两个时间点的差异
snapshot2 = tracemalloc.take_snapshot()
diff = snapshot2.compare_to(snapshot, "lineno")
print("\n=== 增量 Top 10 ===")
for stat in diff[:10]:
print(stat)
4.5 实战:排查内存泄漏
python
import tracemalloc
import gc
class LeakyCache:
"""模拟有内存泄漏的缓存"""
def __init__(self):
self._cache = {}
def add(self, key, value):
self._cache[key] = value
# 故意不实现 remove ------ 导致缓存无限增长
def find_leaks():
tracemalloc.start()
cache = LeakyCache()
# 模拟生产流量
for i in range(10000):
cache.add(f"key_{i}", "x" * 1000)
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics("traceback")
print("=== 内存占用最高的 5 个 traceback ===")
for stat in top_stats[:5]:
print(f"{stat.count} blocks: {stat.size / 1024:.1f} KiB")
for line in stat.traceback.format()[:3]: # 只显示前 3 行
print(f" {line}")
# 输出会精确定位到 cache.add 的调用位置
# 从而发现 LeakyCache 没有容量上限
五、📊 py-spy:无侵入式分析
py-spy 是一个采样式(Sampling)性能分析器 ,最大的优势是无需修改代码、无需暂停程序。
5.1 适用场景
- 生产环境:不能重启服务的场景
- 卡死程序 :程序已经卡住,无法插入
cProfile代码 - 长时间运行的服务:Web 服务器、消息队列消费者
5.2 安装与使用
bash
pip install py-spy
分析正在运行的进程:
bash
# 找到 Python 进程的 PID
ps aux | grep python
# 实时查看调用栈
py-spy top --pid 12345
# 生成火焰图
py-spy record -o flamegraph.svg --pid 12345 --duration 30
启动时直接分析:
bash
py-spy record -o profile.svg -- python my_script.py
5.3 py-spy vs cProfile 对比
| 特性 | py-spy | cProfile |
|---|---|---|
| 原理 | 采样 (Sampling) | 确定性 (Instrumentation) |
| 性能影响 | ~1-2% | 20%-50% |
| 需要修改代码 | 否 | 是 |
| 生产环境使用 | ✅ 安全 | ❌ 建议避免 |
| 逐行精度 | 函数级 | 函数级 |
| 支持火焰图 | 原生 | 需借助 snakeviz |
| 内存分析 | 否 | 否 |
💡 策略建议:
- 本地开发/测试:使用 cProfile 获取精确数据
- 生产环境排查:使用 py-spy 做到零侵入
六、🔬 实战案例:完整的性能优化流程
场景:日志分析系统
假设我们有一个处理千万级日志的系统,用户反馈处理太慢。
python
import re
from collections import Counter
from datetime import datetime
from typing import List, Dict
def parse_log_line(line: str) -> Dict:
"""解析单行日志"""
pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'
match = re.match(pattern, line)
if match:
return {
"timestamp": datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S"),
"level": match.group(2),
"message": match.group(3)
}
return None
def process_logs(filepath: str) -> Dict:
"""处理日志文件"""
level_counts = Counter()
error_messages = []
hourly_stats = Counter()
with open(filepath, "r", encoding="utf-8") as f:
for line in f:
parsed = parse_log_line(line)
if not parsed:
continue
level_counts[parsed["level"]] += 1
if parsed["level"] == "ERROR":
error_messages.append(parsed["message"])
hour_key = parsed["timestamp"].strftime("%Y-%m-%d %H:00")
hourly_stats[hour_key] += 1
return {
"total": sum(level_counts.values()),
"by_level": dict(level_counts),
"errors": error_messages[:100],
"hourly": dict(hourly_stats.most_common(24))
}
if __name__ == "__main__":
result = process_logs("app.log")
print(f"Processed {result['total']} lines")
Step 1:cProfile 宏观定位
bash
python -m cProfile -o log_profile.prof log_analyzer.py
snakeviz log_profile.prof
通过可视化火焰图发现:
parse_log_line占总耗时 62%- 其中
datetime.strptime占parse_log_line的 70% re.match占parse_log_line的 20%
Step 2:line_profiler 微观分析
python
@profile
def parse_log_line(line: str) -> Dict:
pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'
match = re.match(pattern, line)
if match:
return {
"timestamp": datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S"),
"level": match.group(2),
"message": match.group(3)
}
return None
结果确认:
datetime.strptime占总线耗时的 55%re.match占 25%
Step 3:针对性优化
优化策略:
datetime.strptime→ 使用time.mktime+ struct_time(快 3-5 倍)- 预编译正则表达式(
re.compile) - 格式化字符串使用缓存
python
import re
import time
import calendar
from collections import Counter
from typing import List, Dict, Optional
# 预编译正则
LOG_PATTERN = re.compile(
r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'
)
# 解析日期缓存 --- 避免重复解析相同日期
_DATE_CACHE = {}
def _parse_date(date_str: str) -> time.struct_time:
"""缓存 + 轻量解析替代 strptime"""
if date_str not in _DATE_CACHE:
parts = date_str.split("-")
_DATE_CACHE[date_str] = (
int(parts[0]), int(parts[1]), int(parts[2])
)
return _DATE_CACHE[date_str]
def parse_log_line_optimized(line: str) -> Optional[Dict]:
match = LOG_PATTERN.match(line)
if not match:
return None
y, m, d = match.group(1).split("-")
h, mi, s = match.group(2).split(":")
timestamp = calendar.timegm((
int(y), int(m), int(d), int(h), int(mi), int(s)
))
return {
"timestamp": timestamp, # 存储为数字而非 datetime 对象
"level": match.group(3),
"message": match.group(4)
}
Step 4:memory_profiler 排查内存
python
@profile
def process_logs_optimized(filepath: str) -> Dict:
# ... 优化后的实现,关注 error_messages 的内存占用
发现:
error_messages存储了所有错误日志的全部内容- 如果错误率 5%,1 亿行日志会有 500 万条错误信息 → 可能导致 OOM
优化:
python
# 只存储前 100 条错误样本,其余只记录计数
MAX_ERROR_SAMPLES = 100
Step 5:优化结果对比
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 处理 100 万行耗时 | 12.3s | 2.1s | 5.9x |
| 峰值内存 | 450 MiB | 85 MiB | 5.3x |
| 日期解析 | 6.5s | 0.3s | 21.7x |
七、🧰 工具选择决策树
你的需求是?
│
├─ 定位 CPU 热点
│ ├─ 可以插装代码 → cProfile + snakeviz
│ └─ 不能插装/生产环境 → py-spy
│
├─ 定位哪行代码慢
│ └─ line_profiler
│
├─ 排查内存问题
│ ├─ 整体内存画像 → memory_profiler + mprof
│ └─ 精确追踪分配 → tracemalloc
│
└─ 实时监控/On-Call
└─ py-spy top
八、⚡ 性能优化通用原则
8.1 优先选择
python
# ❌ 慢
result = [i ** 2 for i in range(1000)]
# ✅ 快(约 2x)
import math
result = [math.pow(i, 2) for i in range(1000)]
# ✅ 更快(约 5x)
result = [i * i for i in range(1000)]
8.2 局部引用加速
频繁访问全局变量或模块属性时,绑定为局部变量:
python
# ❌ 每次循环都去全局查找 len
def total_length(strings):
count = 0
for s in strings:
count += len(s) # 全局查找 len
return count
# ✅ 局部引用
def total_length_fast(strings):
count = 0
len_local = len # 绑定到局部
for s in strings:
count += len_local(s)
return count
8.3 善用内置数据结构
python
# ❌ 列表去重(O(n²))
unique = []
for x in large_list:
if x not in unique:
unique.append(x)
# ✅ 使用 set(O(n))
unique = list(set(large_list))
# ✅ 保持顺序的 O(n) 去重
unique = list(dict.fromkeys(large_list))
九、常见陷阱与注意事项
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| Profile 本身的开销 | cProfile 会让程序慢 2-10 倍 | 生产环境使用 py-spy 采样 |
| 第一次运行 vs 后续运行 | JIT 缓存、文件系统缓存导致差异 | 运行多次取中位数 |
| 垃圾回收干扰 | GC 可能在任何时候触发,导致 time 测量不准 | 多次测量,使用 timeit |
| 内存 profiler 的 overhead | 逐行内存分析本身消耗大量内存 | 先在小样本上分析,再推断全量 |
| I/O 等待被误判为 CPU 密集 | cProfile 不区分 CPU 和 I/O 时间 |
结合 time 命令或 py-spy 观察 |
| 函数调用次数过高的误判 | sorted() 等内置函数的 ncalls 可能误导 |
关注 cumtime 而非 ncalls |
十、总结
核心工具链一览
| 工具 | 分析维度 | 安装方式 | 推荐场景 |
|---|---|---|---|
| cProfile | CPU 函数级 | 标准库 | 快速定位瓶颈函数 |
| SnakeViz | cProfile 可视化 | pip install snakeviz |
分析 cProfile 输出 |
| line_profiler | CPU 行级 | pip install line_profiler |
精确到行 |
| memory_profiler | 内存行级 | pip install memory_profiler |
排查内存占用 |
| tracemalloc | 对象分配追踪 | 标准库 | 精确查找内存泄漏 |
| py-spy | CPU 采样 | pip install py-spy |
生产环境零侵入分析 |
优化工作流
1. 用户反馈慢 → 确认是否复现
2. cProfile + SnakeViz → 找到热点函数
3. line_profiler → 定位到慢的行
4. 实施优化(算法/缓存/局部引用)
5. 再次 Profile 对比 → 确认提升幅度
6. memory_profiler → 确认没有副作用(内存泄漏)
7. 提交 PR,附上优化前后的 Profile 对比
结尾
📌 如果本文对你有帮助,欢迎点赞收藏,关注博主获取更多 Python + AI 实战教程!
📕个人领域 :Linux/C++/java/AI🚀 个人主页 :有点流鼻涕 · CSDN
💬 座右铭 : "向光而行,沐光而生。"
