为什么相对导入在未先以模块导入包时不起作用
我在研究相对导入时发现了一个很有意思的案例,详细内容可以在这里找到:
$ mkdir project
$ cat >> project/one.py
from . import two
$ touch project/two.py
$ python project/one.py
Traceback (most recent call last):
File "project/one.py", line 1, in <module>
from . import two
ImportError: attempted relative import with no known parent package
作者解释说:
你必须确保在包(pkg)被导入之前,它的内容才能相互进行相对导入。有很多方法可以做到这一点,但一般来说,你希望程序首先进行一次绝对导入。
接着,作者展示了以下解决方案:
最简单的方法是将脚本作为模块运行,使用包的名称而不是源代码文件的名称。这样运行就不会出错:
$ python -m project.one
实际上,如果我们想要一个代码的“入口点”,我们应该使用一个在包外的驱动脚本,这样可以通过绝对导入找到这个包,并使用其中的内容:
$ cat >> driver.py
import project.one
$ python driver.py
之所以能成功,是因为driver.py所在的文件夹在模块搜索路径(sys.path)中,这与我们启动Python的方式有关——所以项目文件夹可以直接在当前工作目录(CWD)中找到。
有没有人能详细解释一下,为什么必须先将包作为模块导入,才能在这个包内进行相对导入?这个要求在规范中是在哪里定义的(如果有链接就更好了)?
谢谢
1 个回答
可以查看这个链接了解更多信息:https://docs.python.org/3/reference/import.html#the-import-system.
就像相对路径是根据当前工作目录来解析的,相对导入也是根据当前包来解析的,这个当前包是由__package__
的值来决定的。当你把one.py
当作脚本执行时,__main__.__package__
被设置为None
,这就导致.
不能被当作一个模块来解析。(记住,__main__
是由这个脚本定义的“模块”。)
当你导入one
时,one.__package__
被设置为'project'
,这就是为什么from . import two
会被解析为from project import two
的原因。
使用python -m project.one
的效果和python -c 'import project.one'
差不多(我不太确定这完全正确,但我觉得这样解释__package__
的设置是足够的)。