《Python编程实战》14.1 Click / Typer 构建 CLI
把脚本能力包成真正的命令行工具:用 Click 8.5.0 的装饰器、命令组与共享上下文声明参数与子命令,再用 Typer 0.27.3 的类型标注重写同一个工具,真跑帮助、统计、词频与退出码,并用 CliRunner 把 CLI 层测透,完成工程化的分层与测试。
category
把脚本能力包成真正的命令行工具:用 Click 8.5.0 的装饰器、命令组与共享上下文声明参数与子命令,再用 Typer 0.27.3 的类型标注重写同一个工具,真跑帮助、统计、词频与退出码,并用 CliRunner 把 CLI 层测透,完成工程化的分层与测试。
用本机实测数字拆解多进程的三笔开销:fork/spawn/forkserver 的进程启动成本、跨进程 pickle 序列化税、Queue 与 Pipe 的通道差异;再用 shared_memory 实测 numpy 数组的零拷贝共享,最后给出线程池与进程池的选型决策表。
先如实交代本机并非自由线程构建,再从 PEP 703 的对象头改造、偏向引用计数、延迟引用计数与 Python 临界区入手,讲清 CPython 如何在拿掉 GIL 后维持内存安全,并给出 C 扩展 Py_mod_gil 迁移与生态评估的完整框架。
从 CPython 源码机制出发拆解 GIL:解释器循环为何必须持有全局锁、切换间隔怎样在安全点强制释放线程,并用本机 10 核实测数据画出 CPU 密集与 I/O 密集的影响边界,顺带用实测纠正「counter += 1 会丢更新」这一流传很广的误解。
脚本要有人在对的时间触发:本节讲透 cron 表达式与 crontab 写法、systemd timer 单元文件(Linux 配置,本机 macOS 未实测)、launchd plist,并用 fcntl 文件锁真跑防重入、用 state 文件做幂等、日志落盘。
把手工填表交给脚本:本节用 openpyxl 3.1.5 读写 Excel(样式、公式、只读模式),python-docx 1.2.0 生成 Word 与模板占位符替换,pypdf 6.19.0 合并拆分与提取文本,全部真跑并贴真实输出,附公式不自动计算的坑。
文件批处理最怕「一跑就毁数据」:本节用 pathlib / shutil / hashlib 真跑批量重命名、按规则归类、内容哈希去重与空目录清理,并讲透「先干跑再执行」的工程纪律、原子重命名与跨盘移动,以及 os.walk 对比 Path.walk 的取舍。
本节用标准库 tracemalloc 做快照对比,把一次真实泄漏定位到具体行:两次各 500 个事件后,泄漏行的分配增量是 +527.9 KiB、+500 次分配;再用 objgraph 的 show_growth 与 find_backref_chain 画出 module→globals→_REGISTRY→Session 的引用链,并说明本机无 graphviz 时只能用文本 API。
本节用 sys.getsizeof 逐类实测 int/str/list/dict/tuple 与自定义实例的字节数,讲清 PyObject 头 16 字节、PyVarObject 与容器指针数组的布局,并给出一个反直觉结论:__slots__ 实例本体反而比 __dict__ 实例更大,省下的是整块 __dict__;再用 100 万实例实测 92.0MB 对 53.8MB 的真实差距。
本节拆开 CPython 的引用计数与循环 GC:用 sys.getrefcount 实测引用数随赋值、入容器、传参的增减,看清循环引用为何让 refcount 归零失败;再用 gc 模块实测三代阈值 (2000,10,10)、各代 collect 的回收数、__del__ 与 gc.garbage 的边界,并给出关闭 GC 后循环垃圾堆积的真实 RSS 数字。
把采集从「能跑」做到「能长期跑」:用 urllib.robotparser 实测解析 robots.txt,真跑令牌桶限速器与指数退避重试,用 JSONL 做断点续采,最后收束成一页合规边界清单——只抓公开数据、尊重 robots 与 ToS、控制频率、不绕过反爬。
先跑通一个 JS 渲染的本地动态页,看清「静态解析抓不到」的真相,再用内嵌 JSON 与底层 API 两条替代路线实测拿到数据;浏览器自动化(Playwright / Selenium)本机未安装,只给伪代码、选型对比与等待策略,并划清与反爬的边界。
从 requests 2.34.2 与 httpx 0.28.1 的取舍讲起,实测连接复用、超时分层与重试策略,再用 http.server 起本地假站点,跑通 BeautifulSoup 4.15.0 + lxml 6.1.3 的 CSS 选择器与 XPath,并处理中文乱码与 httpx 系统代理两个真坑。
先实测本机 3.14.6 是否启用 JIT(sys._jit.is_available() 为 False),再拆开 CPython 的三层执行体系:自适应专门化字节码、Tier 2 微操作 IR 与 copy-and-patch 机器码,并梳理 3.13 引入、3.14 进入官方二进制的 JIT 演进与 tail-call 解释器。
实测运行时代码改写的三条路径:直接改 co_code / co_consts 与 CodeType.replace、用 bytecode 0.19.1 在指令层注入调用计数器,以及 3.12+ 的 sys.monitoring。含栈平衡校验边界与三条路径的开销对照(settrace 5.7 倍 vs monitoring 1.7 倍)。
深入 CPython 执行内核:栈式虚拟机的求值栈与指令编码、dis 的栈效应计算、类与推导式的代码生成、异常表机制,以及 3.11+ 自适应专门化如何在运行时把 LOAD_ATTR 改写为 LOAD_ATTR_INSTANCE_VALUE,附 3.14 字节码变化实测对照。
把抽取-转换-加载拼成一条可重跑、有依赖的数据管道:讲透幂等的三种落地手段,用标准库手写一个带拓扑排序与幂等状态的 DAG 调度器并真跑,再用 Polars 原生 Parquet 做分区落盘,最后收束成一份数据质量校验、水位线监控与可观测清单。
用同一份 50 万行真实数据实测 Polars 2.0:LazyFrame 惰性求值与查询计划、投影与谓词下推、多线程并行与流式引擎、表达式 API 与 pandas 的对照迁移,并给出同一任务的耗时对比,最后讲清什么时候该换、什么时候别换。
用 50 万行真实数据集实测 pandas 3.0 的性能陷阱:Copy-on-Write 成为默认后链式赋值的真实行为、SettingWithCopyWarning 的现状、dtype 与内存占用、apply 与向量化的耗时差距、groupby 与 merge 的坑,以及分块读取的取舍。
本节从字符串拼接式的代码生成升级到 AST 变换:用 compile() 的三种模式、ast.NodeTransformer 与 fix_missing_locations 把 assert 改写成显式检查,并注入函数计时。实测改写前后的 dis 字节码差异,以及为什么改写版能在 python -O 下继续生效,而原始 assert 会被剥离。