PEP 366 – 主模块显式相对导入
- 作者:
- Alyssa Coghlan <ncoghlan at gmail.com>
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2007年5月1日
- Python版本:
- 2.6, 3.0
- 历史记录:
- 2007年5月1日,2007年7月4日,2007年7月7日,2007年11月23日
摘要
本PEP提出了一种向后兼容的机制,允许在包内的可执行模块中使用显式相对导入。由于PEP 328和PEP 338之间笨拙的交互,此类导入目前会失败。
通过添加一个新的模块级属性,本PEP允许在使用-m
开关执行模块时自动工作相对导入。模块本身中少量样板代码将允许在通过名称执行文件时相对导入工作。
Guido于2007年11月接受了PEP [5]。
提议的更改
主要提议的更改是引入了一个新的模块级属性,__package__
。当它存在时,相对导入将基于此属性而不是模块__name__
属性。
与当前的__name__
属性一样,设置__package__
将是用于导入模块的PEP 302加载器的责任。使用imp.new_module()
创建模块对象的加载器将自动将新属性设置为None
。当导入系统在没有设置__package__
(或将其设置为None
)的模块中遇到显式相对导入时,它将计算并存储正确的值(对于普通模块为__name__.rpartition('.')[0]
,对于包初始化模块为__name__
)。如果__package__
已经设置,则导入系统将优先使用它,而不是从__name__
和__path__
属性重新计算包名称。
runpy
模块将显式设置新属性,将其基于用于定位要执行的模块的名称,而不是用于设置模块__name__
属性的名称。这将允许使用-m
开关从主模块正确执行相对导入。
当主模块由其文件名指定时,__package__
属性将设置为None
。为了允许在直接执行模块时进行相对导入,在第一个相对导入语句之前需要类似以下的样板代码
if __name__ == "__main__" and __package__ is None:
__package__ = "expected.package.name"
请注意,此样板代码仅在顶级包已可以通过sys.path
访问时才足够。为了使直接执行在没有顶级包可导入的情况下工作,需要其他操作sys.path
的代码。
这种方法也与使用同级模块的绝对导入具有相同的缺点 - 如果脚本移动到不同的包或子包,则需要手动更新样板代码。它的优点是此更改只需要对每个文件进行一次,而不管相对导入的数量。
请注意,将__package__
显式设置为空字符串是允许的,并且会禁用该模块的所有相对导入(因为在这种情况下,导入机制会将其视为顶级模块)。这意味着像runpy
这样的工具在设置__package__
时不需要为顶级模块提供特殊情况处理。
更改的理由
目前无法从主模块使用显式相对导入的问题至少在一个开放的SF错误报告(#1510172)[1]中有所讨论,并且很可能是comp.lang.python上至少一些查询(例如Alan Isaac在[2]中的问题)的一个因素。
本PEP旨在提供一个解决方案,允许从主模块进行显式相对导入,而不会在解释器启动或普通模块导入期间产生任何重大成本。
PEP 338中关于相对导入和主模块的部分提供了关于此问题的更多详细信息和背景。
参考实现
SVN中的修订版47142实现了本提案的早期变体,该变体将主模块的真实模块名称存储在__module_name__
属性中。由于2.5当时已经处于测试阶段,因此它被还原了。
补丁1487 [4]是本PEP的提议实现。
备选方案
PEP 3122提议通过更改主模块的识别方式来解决此问题。为了修复在整体方案中是一个非常小的错误,而要承担如此大的兼容性成本,这代价太高了,并且该PEP被拒绝了[3]。
本PEP中提案的优势在于,它对普通代码的唯一影响是在导入模块时设置额外属性所需的一点点时间。相对导入本身应该会稍微加快速度,因为包名称缓存在模块全局变量中,而不是必须为每个相对导入再次计算。
参考文献
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0366.rst
上次修改时间:2023年10月11日12:05:51 GMT