CPython lzma 模块完全指南:liblzma 压缩接口、.xz/.lzma 文件读写与自定义过滤器链
本文以 CPython 官方库文档 Doc/library/lzma.rst 为核心骨架,系统讲解标准库
lzma模块在 Python 中的完整用法:包括文件级 APIlzma.open()/LZMAFile读写.xz、.lzma与裸压缩流,内存级增量压缩LZMACompressor/LZMADecompressor,以及面向高级场景的过滤器链(Filter Chain)配置与全部模块常量。读完本文,你将能够用 Python 直接读写 xz 工具产出的压缩包、处理拼接式多流文件、按需设置压缩预设与内存上限,并深入理解lzma模块背后“纯 Python 文件接口 + C 扩展_lzma增量编解码器”的分层实现(对应源码 Lib/lzma.py 与 Modules/_lzmamodule.c)。
一、模块概览:lzma 是什么
lzma 是 Python 标准库中基于 liblzma 压缩库(即 xz 工具背后所用的 XZ Utils 库)的封装模块。自 Python 3.3 引入以来(文档注明 versionadded: 3.3),它为开发者提供了三类能力:
- 类与便捷函数:用于压缩与解压内存中的字节数据;
- 文件接口:支持
.xz容器格式、遗留的.lzma容器格式(由命令行工具xz使用),以及不带任何容器头的裸压缩流(raw stream); - 完整的常量体系:包括容器格式、完整性校验类型、压缩预设与过滤器 ID 等。
从接口风格上讲,文档明确指出 “该模块提供的接口与 bz2 模块非常相似”。从当前仓库(main 开发分支)的源码结构看,这一相似性得到了更深的体现:LZMAFile 与 bz2.BZ2File、gzip 的实现都迁移到了共享的 compression._common._streams 基础设施上,复用了 Lib/compression/_common/_streams.py 中的 BaseStream、DecompressReader 等流抽象类,并把模块本体挂入新的 compression 命名空间包(见 Lib/compression/lzma.py,它负责从顶层 lzma 模块重导出全部公开符号)。
线程安全提示
文档特别提醒:LZMAFile 与 bz2.BZ2File 不是线程安全的。如果要在多个线程中共用同一个 LZMAFile 实例,必须用锁(如 threading.Lock)对其进行保护,或为每个线程各自创建独立实例。
关于可选模块与发行版差异
lzma 属于可选模块(optional module)。若你的 Python 安装中缺少它,文档建议查阅发行方(即提供该 Python 构建的一方)的文档;对发行方而言则可参考仓库内 Doc/includes/optional-module.rst 指向的 optional-module-requirements 说明。底层对应关系可见构建配置文件 Modules/Setup.stdlib.in:_lzma _lzmamodule.c 一行显示该扩展需链接系统 liblzma(对应注释中 -llzma),因此系统缺少 liblzma 开发库或链接的是受限功能版 liblzma 时,部分能力(如特定校验算法)会不可用。
分层实现:Python 文件层 + C 增量编解码器
当前仓库的实现可概括为两层(这是从 Lib/lzma.py 源码可以直接读到的结构):
| 层次 | 实现位置 | 职责 |
|---|---|---|
| 高层文件接口 | Lib/lzma.py(LZMAFile、open、compress、decompress 均为纯 Python) |
文件对象封装、文本模式、seek 模拟、多流拼接触发器 |
| 底层增量编解码器 | Modules/_lzmamodule.c 的 _lzma C 扩展 |
LZMACompressor、LZMADecompressor、LZMAError、全部常量、is_check_supported() |
Lib/lzma.py 第 29~30 行通过 from _lzma import * 直接导入 C 扩展的压缩器/解压器类型与常量,因此 Python 层无需关心 liblzma 的底层调用细节。
二、异常类型:LZMAError
模块定义了一个异常类型:
import lzma
try:
lzma.compress(b"data", format=lzma.FORMAT_ALONE, check=lzma.CHECK_CRC32) # ALONE 不支持完整性校验
except lzma.LZMAError as e:
print("压缩失败:", e)
lzma.LZMAError 在压缩、解压出错,或初始化压缩器/解压器状态时抛出。在 C 源码中它通过 PyErr_NewExceptionWithDoc("_lzma.LZMAError", ...) 创建(见 Modules/_lzmamodule.c 中 lzma_exec 函数),Python 层再将其原样重导出为 lzma.LZMAError。
典型触发场景包括:
- 指定了不支持的完整性校验类型(如对
FORMAT_ALONE使用CHECK_CRC32); - 解压时内存超出
memlimit限制; - 数据损坏、截断(解压未达流结束标记);
- 使用
FORMAT_RAW却未提供filters过滤器链。
三、读取与写入压缩文件
3.1 函数签名与总体行为
lzma.open(filename, mode="rb", *, format=None, check=-1, preset=None,
filters=None, encoding=None, errors=None, newline=None)
lzma.open() 以二进制模式或文本模式打开一个 LZMA 压缩文件,返回一个 file object。它的核心行为在 Lib/lzma.py 的 open() 函数中有清晰实现:先剔除模式字符串中的 "t" 以构建 LZMAFile,二进制模式直接返回该对象;文本模式则再包一层 io.TextIOWrapper,并用 io.text_encoding(encoding) 处理默认编码(源码见 Lib/lzma.py)。
filename 参数可以是:
- 一个真正的文件名:
str、bytes或 path-like 对象(实现了os.PathLike),此时打开该命名的文件; - 一个已打开的 file object:直接在该对象上进行读写。
mode 参数可取如下值:
| 类别 | 取值 | 说明 |
|---|---|---|
| 二进制模式 | "r" / "rb" |
读(默认) |
| 二进制模式 | "w" / "wb" |
覆盖写 |
| 二进制模式 | "x" / "xb" |
独占创建(文件已存在则报错) |
| 二进制模式 | "a" / "ab" |
追加写 |
| 文本模式 | "rt" |
文本读 |
| 文本模式 | "wt" |
文本覆盖写 |
| 文本模式 | "xt" |
文本独占创建 |
| 文本模式 | "at" |
文本追加 |
默认 mode 为 "rb"。模式的历史演进见文档版本注记:"x"、"xb"、"xt" 模式自 Python 3.4 起支持;3.6 起 filename 接受 path-like 对象。
参数语义随打开方式而变(这一点与 LZMAFile、LZMACompressor、LZMADecompressor 保持一致):
- 读模式下,
format与filters的含义等同于LZMADecompressor;此时不应使用check与preset(若传了会在底层报ValueError); - 写模式下,
format、check、preset、filters的含义等同于LZMACompressor。
二进制模式下 lzma.open(filename, mode, ...) 与 LZMAFile(filename, mode, ...) 构造器完全等价;此时不得提供 encoding、errors、newline。文本模式则基于 LZMAFile 外层包裹 io.TextIOWrapper,按指定的编码、错误处理策略与换行符规则工作。
3.2 LZMAFile 类:面向二进制文件对象的完整封装
lzma.LZMAFile(filename=None, mode="r", *, format=None, check=-1,
preset=None, filters=None)
LZMAFile 以二进制模式打开 LZMA 压缩文件。它的关键设计点如下:
① 包装 vs 自开文件。 filename 既可以是已打开的文件对象(此时 LZMAFile 关闭时不会关闭被包装的底层文件),也可以是文件名。当前实现中,当 filename 是 str/bytes/os.PathLike 时会以 builtins.open 自行打开并置 _closefp=True,否则视为已打开的文件对象(见 Lib/lzma.py)。
② mode 语义。 mode 可以是 "r"(默认,读)、"w"(覆盖写)、"x"(独占创建)、"a"(追加),也可以等价写成 "rb"、"wb"、"xb"、"ab"。特别地:若 filename 是文件对象(而非文件名),"w" 不会截断该文件,而是等价于 "a"(因为追加写发生在当前文件位置)。"x" 与 "xb" 模式自 3.4 起支持。
③ 读取多流拼接文件。 读模式下输入文件可以是多个独立压缩流的拼接(例如用 xz file 与 cat a.xz b.xz > c.xz 式操作拼出的文件),LZMAFile 会把它们当作一条逻辑流透明解码。Python 层实现通过 DecompressReader + io.BufferedReader 完成(见 Lib/lzma.py)。写模式的格式默认值为 FORMAT_XZ,读模式默认值为 FORMAT_AUTO(见 Lib/lzma.py)。
④ 支持的接口范围。 LZMAFile 支持 io.BufferedIOBase 的全部成员,除了 detach() 与 truncate();支持迭代与 with 语句。它额外提供:
peek(size=-1):返回缓冲数据但不推进文件位置。除非已达 EOF,否则至少返回 1 字节;精确返回多少字节未定义(size参数被忽略)。注意:调用peek不改变LZMAFile自身的位置,但可能改变底层文件对象的位置(例如当filename传入的是一个文件对象时)。mode属性(3.13 新增):读模式为'rb',写模式为'wb'。当前实现中以_MODE_READ/_MODE_WRITE内部标记推导(见 Lib/lzma.py)。name属性(3.13 新增):lzma 文件名,等价于底层 file object 的FileIO.name。
⑤ 读写语义与 seek。 write(data) 返回未压缩数据的字节数(即 len(data)),数据因缓冲未必立即落盘,直到 close() 时才会写入压缩器 flush() 的余量(见 Lib/lzma.py)。seek() 是被模拟的(基于 BufferedReader 解压重放),因此某些参数组合下可能极慢;文档同时注明 3.5 起 read() 接受 None 参数。
3.3 文件读写实战示例
文档中的示例均可直接运行,以下是按场景组织的完整示范:
读取压缩文件:
import lzma
with lzma.open("file.xz") as f:
file_content = f.read()
创建压缩文件:
import lzma
data = b"Insert Data Here"
with lzma.open("file.xz", "w") as f:
f.write(data)
向一个已打开的文件中写入压缩数据(压缩与非压缩内容混写):
import lzma
with open("file.xz", "wb") as f:
f.write(b"This data will not be compressed\n")
with lzma.open(f, "w") as lzf:
lzf.write(b"This *will* be compressed\n")
f.write(b"Not compressed\n")
说明:上面第三段示例向已打开的 file object 写入。由于
LZMAFile包装文件对象时不会接管其关闭权,with退出只冲刷压缩器,不会关掉外层的f;且如文档所述,此场景下"w"不截断文件,实际等价于追加。
使用自定义过滤器链创建压缩文件:
import lzma
my_filters = [
{"id": lzma.FILTER_DELTA, "dist": 5},
{"id": lzma.FILTER_LZMA2, "preset": 7 | lzma.PRESET_EXTREME},
]
with lzma.open("file.xz", "w", filters=my_filters) as f:
f.write(b"blah blah blah")
四、在内存中压缩与解压数据
4.1 LZMACompressor:增量压缩器
lzma.LZMACompressor(format=FORMAT_XZ, check=-1, preset=None, filters=None)
LZMACompressor 用于增量压缩。若只需一次性压缩一小段数据,应优先使用便捷函数 lzma.compress()。
各参数语义:
format:指定容器格式,可取FORMAT_XZ(默认)、FORMAT_ALONE、FORMAT_RAW。在 C 扩展的参数解析中,format的默认值即为FORMAT_XZ(见 Modules/_lzmamodule.c)。check:写入压缩数据中的完整性校验类型,解压时用于确认数据未被损坏。可取CHECK_NONE、CHECK_CRC32、CHECK_CRC64(FORMAT_XZ的默认)、CHECK_SHA256。指定了不支持的校验类型会抛LZMAError。preset与filters二选一:压缩设置要么用预设级别preset简写,要么用filters自定义过滤器链细粒度指定,两者不能同时提供(_lzma会对“同时给出 preset 与 filters”抛错,测试用例见 Lib/test/test_lzma.py 中LZMACompressor(preset=7, filters=[...])的相关断言)。
preset 若提供,应是一个 0 到 9(含)之间的整数,可再按位或上 PRESET_EXTREME。若既不提供 preset 也不提供 filters,默认采用 PRESET_DEFAULT(即预设级别 6)。级别越高输出越小,但压缩越慢。
内存代价警告(文档重点提示): 高预设不仅更耗 CPU,还需要大量内存(解压同样需要更多内存)。例如预设 9 时,一个
LZMACompressor对象的开销可高达 800 MiB。因此一般建议使用默认预设。这与xz工具的行为一致——追求极限压缩比必须付出内存与时间成本。
实例方法:
compress(data):压缩一段bytes数据,返回包含至少部分输入压缩结果的bytes。部分data可能被内部缓冲以用于后续compress()/flush()调用。返回数据应与之前所有compress()的输出拼接。flush():结束压缩,返回压缩器内部缓冲中的剩余数据。调用后压缩器不可再使用。
增量压缩标准范式(文档原例):
import lzma
lzc = lzma.LZMACompressor()
out1 = lzc.compress(b"Some data\n")
out2 = lzc.compress(b"Another piece of data\n")
out3 = lzc.compress(b"Even more data\n")
out4 = lzc.flush()
# 将所有分片结果拼接起来:
result = b"".join([out1, out2, out3, out4])
4.2 LZMADecompressor:增量解压器
lzma.LZMADecompressor(format=FORMAT_AUTO, memlimit=None, filters=None)
LZMADecompressor 用于增量解压;若想一次解压完整个压缩流,用便捷函数 lzma.decompress() 更方便。
参数语义:
format:容器格式。默认FORMAT_AUTO,可同时解压.xz与.lzma文件;也可显式指定FORMAT_XZ、FORMAT_ALONE、FORMAT_RAW。memlimit:解压器可用内存上限(字节)。设置后,若给定内存限制内无法完成解压,将抛LZMAError。这是防御“解压炸弹”的实用手段。filters:创建待解压流所用的过滤器链。当format为FORMAT_RAW时必须提供,其他格式下不应使用。
多流限制(文档重点提示): 与
decompress()、LZMAFile不同,LZMADecompressor不会透明处理包含多个压缩流的输入。要解压多流输入,必须为每个流各创建一个新解压器。
实例方法:
decompress(data, max_length=-1):解压data(bytes-like 对象),返回解压后的bytes。部分数据可能被内部缓冲用于后续调用,返回结果应与之前所有调用的输出拼接。- 若
max_length非负,最多返回max_length字节。若达到上限且仍可产出更多输出,则needs_input会被置为False,此时下一次decompress()可传b''以继续取得剩余输出; - 若输入全部解压并返回(无论是因为小于
max_length,还是max_length为负),needs_input置为True; - 流结束标记之后继续解压会抛
EOFError;流结束标记之后出现的多余数据会被忽略并保存到unused_data属性中。 max_length参数自 3.5 加入。
- 若
实例属性:
| 属性 | 含义 |
|---|---|
check |
输入流所用完整性校验的 ID。在读到足够解码出校验类型的数据前,可能是 CHECK_UNKNOWN |
eof |
是否已到达流结束标记 |
unused_data |
压缩流结束之后发现的数据;流结束前恒为 b"" |
needs_input |
若为 False,表示 decompress() 在需要新输入前还能产出更多解压数据(3.5 新增) |
4.3 单次(一次性)便捷函数
lzma.compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None)
lzma.decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None)
compress(data):压缩bytes数据,返回压缩后的bytes。参数含义同LZMACompressor。其 Python 实现为comp.compress(data) + comp.flush()(见 Lib/lzma.py)。decompress(data):解压bytes,返回解压后的bytes。若data是多个不同压缩流的拼接,则全部解压并返回拼接结果。 其实现是一个循环:反复用LZMADecompressor解压当前数据、检查eof、取unused_data作为下一轮输入,直至没有余量(见 Lib/lzma.py)。参数含义同LZMADecompressor。
内存级压缩的最简用法(文档原例):
import lzma
data_in = b"Insert Data Here"
data_out = lzma.compress(data_in)
五、指定自定义过滤器链(Filter Chain)
当内置预设不足以表达需求时,可把 filters 参数传给压缩器/解压器/文件接口。过滤器链说明符(filter chain specifier)是一个字典组成的序列,每个字典描述一个过滤器,必须含 "id" 键,并可含若干过滤器相关的附加选项键。
一个链条的硬性规则(文档明确):
- 最多 4 个过滤器,且不能为空;
- 最后一个过滤器必须是压缩过滤器;
- 其余过滤器必须是 delta 或 BCJ 过滤器。
5.1 合法过滤器 ID 一览
| 类别 | ID 常量 | 可用容器格式 | 备注 |
|---|---|---|---|
| 压缩过滤器 | FILTER_LZMA1 |
FORMAT_ALONE |
— |
| 压缩过滤器 | FILTER_LZMA2 |
FORMAT_XZ、FORMAT_RAW |
— |
| Delta 过滤器 | FILTER_DELTA |
与任一压缩过滤器配合 | — |
| BCJ 过滤器 | FILTER_X86 |
同上 | 适用于 x86 机器码 |
| BCJ 过滤器 | FILTER_IA64 |
同上 | Itanium |
| BCJ 过滤器 | FILTER_ARM |
同上 | ARM |
| BCJ 过滤器 | FILTER_ARMTHUMB |
同上 | ARM Thumb |
| BCJ 过滤器 | FILTER_POWERPC |
同上 | PowerPC |
| BCJ 过滤器 | FILTER_SPARC |
同上 | SPARC |
| BCJ 过滤器 | FILTER_ARM64 |
同上 | 要求 lzma 版本 ≥ 5.4.0(文档标记为 next 版本新增) |
| BCJ 过滤器 | FILTER_RISCV |
同上 | 要求 lzma 版本 ≥ 5.6.0(文档标记为 next 版本新增) |
除 FILTER_ARM64、FILTER_RISCV 外,其余 BCJ 过滤器在所有 lzma 运行时版本上均可用。
5.2 压缩过滤器选项(LZMA2 / LZMA1)
作为字典的附加键传入。各选项含义与取值范围(文档原述):
| 键 | 含义 | 取值范围 / 默认 |
|---|---|---|
preset |
作为未显式指定选项的默认值来源的压缩预设 | 0~9(可 OR PRESET_EXTREME) |
dict_size |
字典大小(字节) | 4 KiB ~ 1.5 GiB(含) |
lc |
字面量上下文位数(literal context bits) | 与 lp 之和 ≤ 4 |
lp |
字面量位置位数(literal position bits) | lc + lp ≤ 4 |
pb |
位置位数(position bits) | 至多 4 |
mode |
压缩模式 | MODE_FAST 或 MODE_NORMAL |
nice_len |
“好的匹配长度”阈值 | ≤ 273 |
mf |
匹配查找器 | MF_HC3、MF_HC4、MF_BT2、MF_BT3、MF_BT4 |
depth |
匹配查找器最大搜索深度 | 0(默认)表示依据其它选项自动选择 |
这些选项对应 liblzma lzma_options_lzma 结构体的字段——这正是 XZ Utils 的 LZMA SDK 命名体系:dict_size 决定滑窗大小与解压内存,lc/lp/pb 控制上下文建模的比特分配,mf 决定匹配查找算法(HC 系为哈希链,BT 系为二叉树),而 mode 与 nice_len 共同影响压缩速度与比率。
5.3 Delta 过滤器选项
Delta 过滤器存储字节之间的差值,在特定场景(如小整数序列、部分结构化数据)下为压缩器制造更多可压缩的重复输入。它只有一个选项:
dist:被减字节之间的距离,默认1(即相邻字节求差)。示例开篇演示了{"id": lzma.FILTER_DELTA, "dist": 5}的用法。
5.4 BCJ(分支-调用-跳转)过滤器选项
BCJ 过滤器面向机器码设计:把代码中的相对分支、调用与跳转转换为绝对寻址,从而增加压缩器可挖掘的冗余。它们只支持一个选项:
start_offset:应映射到输入数据起始处的地址,默认0。当压缩对象会加载到非零基址时,设置该值可进一步提升压缩率。
5.5 组合示例:内存压缩
import lzma
filters = [
{"id": lzma.FILTER_LZMA2, "preset": 9 | lzma.PRESET_EXTREME,
"dict_size": 64 * 1024, "lc": 3, "lp": 0, "pb": 2},
]
raw = lzma.compress(b"payload" * 1000, format=lzma.FORMAT_RAW, filters=filters)
六、模块常量参考
以下是用于上述类与函数的 format、check、preset、filters 参数的模块级常量。需要指出的是,它们在 C 扩展初始化阶段通过 PyModule_AddIntMacro / module_add_int_constant 从 liblzma 的 LZMA_* 宏直接注册(见 Modules/_lzmamodule.c 的 lzma_exec,其中注释说明部分常量超过 32 位,故用 PyLong_FromLongLong 处理,这解释了 CHECK_ID_MAX 等大数值常量在 32 位 long 平台上的正确注册方式)。
6.1 容器格式常量
| 常量 | 说明 |
|---|---|
FORMAT_XZ |
.xz 容器格式 |
FORMAT_ALONE |
遗留的 .lzma 容器格式。功能受限:不支持完整性校验,也不支持多过滤器 |
FORMAT_RAW |
不使用任何容器格式的裸数据流。不支持完整性校验,且压缩与解压都必须显式提供自定义过滤器链;以该格式压缩的数据无法用 FORMAT_AUTO 解压 |
FORMAT_AUTO |
仅用于解压。自动探测容器格式,从而同时支持 .xz 与 .lzma |
6.2 完整性校验常量
| 常量 | 说明 |
|---|---|
CHECK_NONE |
无完整性校验。是 FORMAT_ALONE 与 FORMAT_RAW 的默认(也是唯一可接受)取值 |
CHECK_CRC32 |
32 位循环冗余校验 |
CHECK_CRC64 |
64 位循环冗余校验。FORMAT_XZ 的默认值 |
CHECK_SHA256 |
256 位安全散列算法 |
CHECK_UNKNOWN |
流的校验类型尚无法确定;LZMADecompressor.check 在解码出足够输入前可能保持此值 |
CHECK_ID_MAX |
受支持的最大完整性校验 ID |
6.3 压缩预设常量
| 常量 | 说明 |
|---|---|
PRESET_DEFAULT |
默认压缩预设,等价于预设级别 6 |
PRESET_EXTREME |
可被按位 OR 到预设级别(0~9)上的标志,用于选择更慢但更彻底的变体 |
6.4 过滤器 ID 与选项常量
- 压缩过滤器:
FILTER_LZMA1(配FORMAT_ALONE)、FILTER_LZMA2(配FORMAT_XZ/FORMAT_RAW)。 - 模式:
MODE_FAST、MODE_NORMAL(作为过滤器说明符的mode选项值)。 - 匹配查找器:
MF_HC3、MF_HC4、MF_BT2、MF_BT3、MF_BT4(作为mf选项值)。
七、运行时能力探测:is_check_supported()
lzma.is_check_supported(check) -> bool
返回给定完整性校验在当前系统上是否受支持。CHECK_NONE 与 CHECK_CRC32 永远受支持;CHECK_CRC64 与 CHECK_SHA256 在你使用的 liblzma 以受限功能集编译时可能不可用。其实现位于 C 扩展的 _lzma.is_check_supported(见 Modules/_lzmamodule.c),直接查询 liblzma 的运行时能力,因此同一安装在不同 liblzma 版本上可能给出不同结果。典型用法是在创建压缩器前先探测目标算法,以便优雅降级:
import lzma
check = lzma.CHECK_SHA256
if lzma.is_check_supported(check):
data = lzma.compress(b"hello", check=check)
else:
data = lzma.compress(b"hello", check=lzma.CHECK_CRC64)
八、进阶场景与完整实战
8.1 防御性解压:设置内存上限
import lzma
data = lzma.compress(b"x" * 10_000_000) # 10 MB 输入
try:
out = lzma.decompress(data, memlimit=64 * 1024 * 1024) # 64 MiB 上限
except lzma.LZMAError:
print("解压所需内存超出限制")
文档说明:一旦传入 memlimit,若无法在限制内完成解压将抛 LZMAError。这对处理不可信输入非常重要——因为如 4.1 节所述,高预设压缩产物在解压时同样需要大内存。
8.2 逐流解压多流输入(LZMADecompressor 版本)
import lzma
def decompress_streams(data):
"""每遇到一个流的结束,就新建解压器处理下一段(unused_data 中的余量)。"""
result = []
while data:
d = lzma.LZMADecompressor()
result.append(d.decompress(data))
if not d.eof:
raise ValueError("数据在流结束标记前截断")
data = d.unused_data
return b"".join(result)
文档明确:LZMADecompressor 不像 decompress()/LZMAFile 那样透明合并多流,因此需要手动按 eof 与 unused_data 切换解压器——上面的模式即官方推荐的逐流处理法。
8.3 流式复制(管道式增量处理)
import lzma
def compress_stream(src, dst, chunk=64 * 1024):
"""将已打开的二进制 src 增量压缩后写入 dst。"""
comp = lzma.LZMACompressor()
while True:
block = src.read(chunk)
if not block:
break
dst.write(comp.compress(block))
dst.write(comp.flush())
with open("plain.bin", "rb") as src, \
lzma.open("plain.bin.xz", "wb") as dst:
compress_stream(src, dst)
九、测试与验证线索
CPython 官方对 lzma 的测试集中在 Lib/test/test_lzma.py,是验证本文所述行为最直接的证据源。可重点参考:
CompressorDecompressorTestCase:验证增量压缩/解压的正确性,包括非法 preset 类型、preset 与 filters 不能同时给定等约束(对应文档 4.1 节的二选一规则);FileTestCase:验证LZMAFile的读写与多流读取,其中test_init_bad_preset覆盖了越界 preset(如10、-1、2**1000、非整数)与读模式禁止 preset 的约束,test_init_with_preset_and_filters再次确认二者互斥;CompressDecompressFunctionTestCase与OpenTestCase:覆盖便捷函数与open()的各类模式/参数组合;MiscellaneousTestCase:覆盖is_check_supported等杂项 API;- 文件底部还定义了原始过滤器链样本(如
FILTERS_RAW_1 = [{"id": lzma.FILTER_LZMA2, "preset": 3}]),用于FORMAT_RAW场景。
十、小结与选型建议
在项目实践中,可以按需在四类入口间选择:
| 需求 | 推荐 API |
|---|---|
读写 .xz/.lzma 磁盘文件 |
lzma.open()(文本/二进制),或 LZMAFile(纯二进制、可包装文件对象) |
| 一次性压缩/解压内存数据 | lzma.compress() / lzma.decompress()(后者自动处理多流拼接) |
| 大数据流式增量处理 | LZMACompressor / LZMADecompressor(注意多流需逐个处理) |
| 精细控制压缩参数 | filters 过滤器链(LZMA2 + Delta + BCJ,最多 4 级) |
记住几条关键纪律:高 preset(尤其 9)内存开销巨大;解压不可信数据务必设 memlimit;LZMAFile/bz2.BZ2File 非线程安全;FORMAT_RAW 压缩的流无法被 FORMAT_AUTO 解压。掌握这些要点后,你在 Python 中处理 XZ 生态数据时便能既高效又安全。
延伸阅读:模块实现 Lib/lzma.py 与 C 扩展 Modules/_lzmamodule.c;共享流基础设施 Lib/compression/_common/_streams.py;构建接入点 Modules/Setup.stdlib.in;接口风格对照 Lib/compression/bz2.py;测试证据 Lib/test/test_lzma.py。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00