Polars在2026年9月2日放出了2.0版本的第一个候选版,安装命令很简单:pip install polars==2.0rc1。官方博客的措辞很松弛,说这次升级"希望是一次无聊的体验",目标不是堆新功能,而是清理历史包袱、把默认值改得更合理。最大的一条默认值变化是:调用LazyFrame.collect()时,引擎会默认解析为流式引擎,官方给出的说法是"聚合意义上轻松5倍"的性能提升。

这条博客读起来确实平静。但翻开随附的完整迁移指南会发现,官方挑出来讲的只有四类变更——流式引擎默认化、is_in类型转换收紧、水平concat严格校验、部分cast被移除。指南里实际列出的破坏性变更至少有十三类。公告和指南之间的落差,才是这次升级真正需要读者注意的地方。

流式引擎默认化:红利和一个不报错的坑

流式引擎能在不把全部数据塞进内存的前提下处理大数据集,这是Polars对标Spark、DuckDB这类系统时的关键筹码。官方早前的PDS-H基准测试显示,新引擎相比旧的内存引擎有3倍到7倍的速度提升,且数据规模越大,提升越明显——这也是"聚合5倍"说法的来源。注意"聚合"两个字,它是跑一批查询的平均值,不是对单条查询的保证,具体收益取决于数据量、算子类型,以及是否触发引擎回退。

真正需要小心的是另一件事:流式引擎默认不保证joingroup_byunpivot等操作的行顺序。旧版本里两个表join之后,结果行的排列是可预期的;2.0之后除非显式传maintain_order=True,或者用pl.Config.set_engine_affinity("in-memory")把整个进程退回旧引擎,否则行序可能和你以为的不一样。

  • 风险.代码不会报错,依然正常跑完,只是结果的行顺序和旧版本不同——这种错误最难在测试里被抓到。

这一点和Polars自我标榜的"严格失败(fail fast)"哲学有点微妙的反差。is_in遇到有损类型转换会主动报错,水平concat遇到长度不一致也会报错,这些都是"显性收紧";但行顺序变化恰恰是一种不主动报错的隐性行为改变,跟"尽早暴露问题"的设计初衷方向相反。

公告只讲了一半,指南里还有九类没提

官方博客给的三个代码示例——is_in把用户ID误判为浮点数精度丢失、水平concat静默补null导致某天的欺诈标记数量对不上、枚举与整数之间的隐式cast——确实是真实存在的坑,而且都能自圆其说地解释为"呼应AI驱动开发下需要更快报错反馈"。这个论述本身没问题,只是它覆盖的变更面太窄。

完整迁移指南里还有一批变更完全没在博客里出现:profile()方法整体移除(因为按节点计时的模型不适配并发的流式执行);explode()对空列表的默认结果从生成null行改成生成0行;有符号整数和UInt64混合运算的结果类型从可能有损的Float64改成Int128;read_csv()因为改走lazy reader路径,导致n_threadsbatch_sizesample_size等一批老参数直接被删除;read_ipc()/scan_ipc()丢掉memory_maprechunk参数;DataFrame Interchange Protocol被整体移除,跨库互操作只能改用to_arrow()to_pandas();melt()改名unpivot()with_row_count()改名with_row_index()且默认列名从row_nr变成index;哈希API从多seed简化为单seed,且不再保证跨版本稳定;多文件CSV扫描时,推断schema默认检查的文件数从全部文件收窄到10个。

这些变更单条看都不算致命,合在一起看,决定了升级到2.0的真实工作量,远比博客里那几个示例暗示的要大。

该现在升级,还是先观察

官方给出的排查思路是:先升级到最新的1.x版本,把所有deprecation警告清干净;再拿2.0rc跑一遍完整测试套件;用collect_schema()在不跑数据的情况下提前校验查询结构;然后在代码里搜profile()melt()with_row_count()这类已知会被移除或改名的字符串,逐一定位。

依赖旧的静默行为、大量使用read_csv老参数或DataFrame Interchange Protocol的生产代码,风险最高——这类代码升级后往往能跑,只是结果和之前不一样,而不是直接报错停下。这类团队更适合先在测试环境里跑一轮完整回归,而不是直接把RC1拉进生产分支。等到正式2.0版本发布、社区反馈过一轮行顺序相关的隐性bug报告之后再跟进,风险会小很多。

公告说无聊,是编辑口吻;迁移指南说小心,才是工程事实。