我想从同一目录中的另一个文件导入一个函数。
有时它对我有用,但有时我会得到:
SystemError: Parent module '' not loaded, cannot perform relative import
有时它与from mymodule import myfunction
一起工作,但有时我也会得到:
SystemError: Parent module '' not loaded, cannot perform relative import
我不懂这里的逻辑,也找不到任何解释。这看起来完全是随机的。
有人能给我解释一下这一切背后的逻辑吗?
解释
来自PEP 328
在某个点上PEP 338与PEP 328冲突:
为了解决这个问题,PEP 366引入了顶级变量^{} :
(强调我的)
如果
__name__
是'__main__'
,则__name__.rpartition('.')[0]
返回空字符串。这就是为什么错误描述中有空字符串文字:CPython的^{} function 的相关部分:
如果在} 访问)中找不到
interp->modules
(可作为^{package
(包的名称),CPython将引发此异常。由于sys.modules
是一个将模块名映射到已加载“的模块的字典,现在很明显,在执行相对导入之前,必须显式绝对导入父模块。注意:来自issue 18018的修补程序添加了another ^{} block ,将在上面的代码之前执行:
如果
package
(同上)是空字符串,则错误消息将是但是,您只能在Python3.6或更新版本中看到这一点。
解决方案1:使用-m运行脚本
考虑一个目录(它是一个Pythonpackage):
包中的所有文件都以相同的两行代码开头:
我把这两条线包括进去,只是为了让手术的顺序更清楚。我们可以完全忽略它们,因为它们不会影响执行。
初始化py和模块py只包含这两行(即,它们实际上是空的)。
standalone.py还尝试通过相对导入导入module.py:
我们很清楚} command line option 运行该模块,该模块将“搜索^{} 指定模块,并将其内容作为
/path/to/python/interpreter package/standalone.py
将失败。但是,我们可以使用^{__main__
模块执行”:-m
为您执行所有导入操作并自动设置__package__
,但是您可以在解决方案2:手动设置软件包
请将其视为概念的证明,而不是实际的解决方案。它不太适合在实际代码中使用。
PEP 366有一个解决此问题的方法,但是它是不完整的,因为仅设置
__package__
是不够的。您需要导入模块层次结构中至少N前面的包,其中N是将搜索要导入的模块的父目录(相对于脚本目录)的数目。因此
将当前模块的前一个Nth的父目录添加到
sys.path
从
中删除当前文件的目录sys.path
使用当前模块的完全限定名导入当前模块的父模块
将
中的完全限定名__package__
设置为2执行相对导入
我将从解决方案1中借用文件并添加更多子包:
这次standalone.py将使用以下相对导入从包包导入模块
我们需要在这一行前面加上样板代码,才能使其工作。
它允许我们按文件名执行standalone.py:
可以找到包装在函数中的更一般的解决方案here。示例用法:
解决方案3:使用绝对导入和setuptools
步骤是-
将显式相对导入替换为等效的绝对导入
安装
package
使其可导入例如,目录结构可以如下
其中setup.py是
T型其余文件是从解决方案1中借用的。
安装将允许您导入包,而不管您的工作目录如何(假设不会出现命名问题)。
我们可以修改standalone.py以使用此优势(步骤1):
将工作目录更改为
project
,然后运行/path/to/python/interpreter setup.py install --user
(--user
在your site-packages directory中安装包)(步骤2):让我们验证现在是否可以将standalone.py作为脚本运行:
注意:如果您决定走这条路,最好使用virtual environments单独安装软件包。
解决方案4:使用绝对导入和一些样板代码
坦率地说,安装是不必要的-您可以添加一些样板代码到您的脚本,使绝对导入工作。
我将从解决方案1中借用文件,并更改standalone.py:
在尝试使用绝对导入从package导入任何内容之前,将package的父目录添加到
sys.path
:用绝对导入替换相对导入:
standalone.py运行无问题:
我觉得我应该警告你:尽量不要这样做,特别是如果你的项目有一个复杂的结构。
顺便说一句,PEP 8建议使用绝对导入,但声明在某些情况下可以接受显式相对导入:
像这样的布局很常见。。。
…像这样的
mymodule.py
。。。…像这样的
myothermodule.py
。。。…还有像这样的
main.py
。。。…当您运行
main.py
或mypackage/mymodule.py
时工作正常,但由于相对导入,mypackage/myothermodule.py
失败。。。你应该用的方式是。。。
……但它有点冗长,不能很好地与
#!/usr/bin/env python3
这样的shebang行混合。对于这种情况,假设名称
mymodule
是全局唯一的,最简单的解决方法是避免使用相对导入,而只使用。。。…但是,如果它不是唯一的,或者包结构更复杂,则需要将包含包目录的目录包含在
PYTHONPATH
中,并按如下方式执行。。。…或者如果你想让它“开箱即用”的话,你可以先用这个来替换代码中的
PYTHONPATH
。。。这是一种痛苦,但有一个线索可以解释为什么在某个吉多·范·罗森写的an email中。。。
在包中运行脚本是否是反模式是主观的,但我个人认为,在包含一些自定义wxPython小部件的包中,它确实很有用,因此我可以为任何源文件运行脚本,以显示仅包含该小部件的
wx.Frame
,用于测试目的。将此文件放入包的初始py文件中:
假设你的包裹是这样的:
现在在包中使用常规导入,例如:
这在python 2和3中都有效。
相关问题 更多 >
编程相关推荐