内容提要
该PEP提议为Python引入冻结显示语法:`f{1,2,3}`生成frozenset,`f{'a':1}`生成frozendict。此语法保证不可变性,使编译器能优化常量折叠,提升性能,并促进不可变容器在并发场景中的使用。语法与现有集合和字典显示一致,新增AST节点和操作码,完全向后兼容。
延伸解读
语法保证是核心优势
与 frozenset({...}) 不同,f{...} 的不可变性由语法本身保证,编译器可据此进行常量折叠等优化。而 frozenset(...) 可能被遮蔽,编译器无法确定其不可变性。这种语法层面的保证也简化了静态分析工具,使其无需名称解析即可确认结果不可变。
对并发与性能的潜在影响
PEP 旨在鼓励使用不可变容器,为自由线程和子解释器做准备。不可变对象可安全共享,减少因意外修改共享容器导致的 bug。编译器可将常量冻结显示折叠为 LOAD_CONST,缓存于 .pyc 文件,提升性能。子解释器已共享 frozenset,未来计划共享 frozendict。
向后兼容与工具影响
f{ 在当前所有 Python 版本中都是语法错误,因此新语法完全向后兼容。但工具链需更新以识别新 token(FLBRACE)、AST 节点和操作码(BUILD_FROZENSET、BUILD_FROZENMAP)。字节码 magic number 将变更,依赖 AST 或字节码的工具需适配。
标准库中的潜在应用
标准库中约有 105 处 frozenset(...) 和 65 处 frozendict(...) 调用,其中约 46 和 22 处传入字面量,可改写为 f{...}。这些调用分布在 typing、dataclasses、functools 等模块。但 PEP 不提议机械重写,仅展示潜在影响。
Q&A
PEP 841 提议的冻结显示语法是什么?
PEP 841 提议为 Python 引入冻结显示语法,即使用 f 前缀加集合或字典显示,例如 f{1, 2, 3} 生成 frozenset,f{'a': 1} 生成 frozendict。
为什么需要冻结显示语法?
因为 Python 目前没有不可变容器的显示语法,创建 frozenset 或 frozendict 需要调用 frozenset({...}) 或 frozendict({...}),编译器无法证明其不可变性,导致无法进行常量折叠等优化。冻结显示语法从语法层面保证不可变性,使编译器可以优化,并促进不可变容器的使用。
冻结显示语法如何提升性能?
由于冻结显示语法保证不可变性,编译器可以将常量冻结显示折叠为单个 LOAD_CONST 指令,并在 .pyc 文件中缓存,从而避免每次执行时重新构建容器,提升性能。
冻结显示语法与现有语法兼容吗?
完全向后兼容。f{ 在当前所有 Python 版本中都是语法错误,因此不会改变现有代码的含义。新增的 FLBRACE 标记、AST 节点和操作码不会影响已有语法。
冻结显示语法对静态分析工具有什么好处?
冻结显示语法为静态分析工具提供了语法上的保证,工具无需进行名称解析即可确定结果是不可变的,从而可以更自信地进行优化或转换,例如将 frozenset({1, 2, 3}) 自动重写为 f{1, 2, 3}。
冻结显示语法如何促进并发编程?
通过提供便捷的语法创建不可变容器,鼓励使用 frozenset 和 frozendict,减少因意外修改共享可变容器而导致的 bug,同时为子解释器和自由线程环境下的对象共享提供支持。
冻结显示语法新增了哪些 AST 节点和操作码?
新增了四个 AST 节点:FrozenSet、FrozenDict、FrozenSetComp、FrozenDictComp;新增了两个操作码:BUILD_FROZENSET 和 BUILD_FROZENMAP。
为什么选择 f 作为前缀?
因为 f 最能唤起“冻结”的联想,且与 f-string 前缀不冲突,因为字符串也是不可变的。其他候选前缀如 i、z、fr 等均被拒绝。