PEP 843:用于DRY再导出的导出语句

PEP 843:用于DRY再导出的导出语句

💡 原文英文,约3500词,阅读约需13分钟。
📝

内容提要

该PEP提议在Python中引入`from <module> export <name>`语句,用于简化库的公共API导出。它自动将导入的名称添加到`__all__`,避免手动同步导入列表和`__all__`的重复工作,并支持别名、通配符和惰性导出。此设计旨在解决大型库(如NumPy、pandas)中常见的导出冗余问题,同时保持向后兼容性。

🔎

延伸解读

解决重复书写问题

大型库如NumPy、pandas在公开API时,常需在导入语句和__all__中重复书写名称,手动同步易出错。此PEP通过一条语句同时完成导入和添加到__all__,消除了重复,降低了维护成本。

与现有方案对比

当前常用`from x import y as y`的反射别名技巧,虽被类型检查器识别,但可读性差且无法控制通配符导入。此PEP提供更清晰的语法,同时保留对`__all__`的控制,避免了自动格式化工具误删导入的问题。

惰性导出的优势

结合PEP 810的惰性导入,`lazy from ._internal.core export PublicAPI`可立即更新`__all__`和`dir()`,而无需加载内部模块。这解决了NumPy等库中`__getattr__`的复杂性问题,使`dir()`和`__all__`自动准确。

适用范围与限制

此PEP专注于模块再导出,不涉及定义处的导出标记或运行时访问限制。对于小型库或单文件模块,现有工具如`atpublic`已足够。此外,`export *`在内部模块无`__all__`时的行为仍待讨论,可能需强制要求显式`__all__`。

Q&A

PEP 843 提出的新语法是什么?

PEP 843 提议引入 `from <module> export <name>` 语句,用于简化库的公共 API 导出。它自动将导入的名称添加到 `__all__`,避免手动同步导入列表和 `__all__`。

PEP 843 解决了什么问题?

它解决了大型库(如 NumPy、pandas)中常见的导出冗余问题:手动维护导入列表和 `__all__` 容易出错且重复,而 `from x import y as y` 的惯用法虽然被类型检查器识别,但可读性差且无法控制通配符导入。

PEP 843 与 PEP 842 有何区别?

PEP 842 提议更广泛的 `export` 关键字,支持五种形式(如 `export NAME`、`export def` 等),并引入 `__export__` 和运行时访问限制。PEP 843 只专注于模块再导出语句,不包含定义点标记或运行时限制。

PEP 843 如何处理通配符导出?

它支持 `from <module> export *`,该形式绑定与 `from <module> import *` 相同的名称,并将这些名称添加到当前模块的 `__all__`。这适用于内部模块已定义 `__all__` 的情况,但若内部模块未定义 `__all__`,则可能意外导出未加下划线的名称,这是开放问题。

PEP 843 如何与惰性导入(PEP 810)结合?

`lazy from <module> export <name>` 会立即将名称添加到 `__all__`,但延迟加载模块,直到名称首次被访问。这有助于大型库在导入时提供完整的 `__all__` 和 `dir()`,而无需立即加载所有内部模块。但 `lazy from <module> export *` 被禁止,因为需要加载模块才能知道导出的名称。

PEP 843 对现有代码和向后兼容性有何影响?

`export` 是一个软关键字,仅在 `from <module>` 之后的位置具有特殊含义,因此现有代码中使用 `export` 作为变量名等不受影响。该语句只影响 `__all__`,而 `__all__` 在所有 Python 版本中都被理解,因此向后兼容。

PEP 843 是否支持别名导出?

是的,支持 `from <module> export <name> as <alias>`,它将别名添加到 `__all__`,而不是原始名称。

PEP 843 与 atpublic 包相比有何优势?

atpublic 的 `@public` 装饰器不适用于导入的名称,其函数调用形式 `public(alias=name)` 需要重复写名称,且不支持 `from x import y as z` 的语法。PEP 843 的语句级导出避免了这些问题,因为它是导入语句的一部分。

🏷️

标签

➡️

继续阅读