python main.py
在 main.py 有相对导入的地方运行将失败。
<root>
└── src
├── main.py # from main.py, want to use the stuff in other.py
└── other.py
$ python main.py
Traceback (most recent call last):
File "main.py", line 2, in <module>
from . other import use_me
ImportError: attempted relative import with no known parent package
- 主文件
# 'import ...' is for absolute import only
# For relative import, must be 'from ... import ...'
# See https://www.python.org/dev/peps/pep-0328/#guido-s-decision
from . other import use_me
if __name__ == "__main__":
use_me()
- 其他.py
def use_me():
print("thanks")
PEP 338 - 将模块作为脚本执行很明显,显式相对导入在主模块中不起作用。
2.5b1 的发布显示了此 PEP 和 PEP 328 之间令人惊讶的(尽管回想起来很明显)交互 -显式相对导入在主模块中不起作用。这是因为相对导入依赖于
__name__
确定当前模块在包层次结构中的位置。在主模块中,值__name__
always'__main__'
,因此显式相对导入总是会失败(因为它们仅适用于包内的模块)。
原因
原因解释如下,解决方法是更新__path__
.
这个问题实际上并不是 -m 开关所独有的。问题是相对导入基于
__name__
,并且在主模块中,__name__
始终具有值__main__
。因此,相对导入目前无法从应用程序的主模块正常工作,因为主模块不知道它真正适合 Python 模块命名空间的位置(这对于通过-m 开关,但直接执行文件和交互式解释器完全不走运)。
通过将以下内容粘贴在模块顶部(在执行任何相对导入之前),您应该能够获得类似于旧的隐式相对导入行为的内容:这使得相对导入机制将您的主模块视为一个包。这种解决方法的问题在于,就像从主模块隐式相对导入的旧情况一样,您最终可能会在 sys.modules 中得到两个不同的兄弟模块副本。一个副本将是
__main__
.foo',而另一个将是 'package.foo'(使用隐式相对导入,第一个副本将是名为 'foo' 的顶级模块)。
if __name__ = '__main__':
from os.path import dirname
__path__ = [dirname(__file__)]
del dirname
相对导入和
__name__
相对导入使用模块的名称属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为' main '),那么无论模块实际位于文件系统上的哪个位置,都将相对导入解析为就好像该模块是顶级模块一样。
PEP 366 似乎启用了将 main.py 作为模块执行的解决方法。
main.py 可以通过以下方式执行:python -m <parent_directory_name>.main
.
这个 PEP 提出了一种向后兼容的机制,允许使用来自包内可执行模块的显式相对导入。由于 PEP 328 和 PEP 338 之间的尴尬交互,此类导入当前失败。
通过添加新的模块级别属性,如果使用 -m 开关执行模块,此 PEP 允许相对导入自动工作。当文件按名称执行时,模块本身的少量样板将允许相对导入工作。
提议的主要更改是引入了一个新的模块级属性package。当它存在时,相对导入将基于此属性而不是模块名称属性。
与当前名称属性一样,设置包将由用于导入模块的 PEP 302 加载器负责。使用 imp.new_module() 创建模块对象的加载器将自动将新属性设置为无。当导入系统在没有设置包(或设置为无)的模块中遇到显式相对导入时,它将计算并存储正确的值(name .rpartition('.')[0] 用于普通模块和名称用于包初始化模块)。如果已经设置了包,那么导入系统将优先使用它,而不是根据名称和路径属性重新计算包名称。
问题
阅读 1510172 和 PEP 366 后,仍然无法准确理解错误发生的机制。
我想有一个模块加载器使用__name__
它看起来代表模块层次结构。它看起来以<root>
下面的文件夹层次结构开始,以模块名称本身结束,但__main__
对于由python <script>.py
.
<root>
└── src
├── main.py # from main.py, want to use the stuff in other.py
└── other.py
这些__name__
, __path__
, __package__
, 模块加载器是如何相关的,幕后到底发生了什么以及它是如何导致问题的?