说我有以下结构:
app/
__init__.py
mod.py
pkg/
__init__.py
submod.py
模块submod
的相对导入为mod
,即:
from .. import mod
我了解如果我想将submod
作为脚本执行,我可以从app/
python -m pkg.submod
但我希望submod.py
成为一个可执行模块,我可以使用
python /path/to/submod.py
我认为PEP-366解决了这个问题,即我在submod
进行任何相对导入之前添加了以下样板代码:
if __name__ == "__main__" and __package__ is None:
__package__ = "app.pkg"
然后我可以回到常规python /path/to/submod.py
。但是,当我这样做时,我得到:
SystemError: Parent module 'app' not loaded, cannot perform relative import
为什么?
最后,我了解一个解决方案是操纵sys.path
中的submod
,以便它可以看到mod1
,然后执行常规import mod1
并避免相对导入。 但如this question所示,这很危险,因为在一个模块中对sys.path
的任何更改都会传播到其他所有内容,因此通常不会篡改{{ 1}}。
有没有办法可以:
或
sys.path
或sys.path
答案 0 :(得分:3)
正如PEP 366在您复制的样板之后明确说明的那样:
请注意,只有顶级包可以通过
sys.path
访问时,此样板才足够。需要操作sys.path
的其他代码才能使直接执行工作,而顶级包不可导入。
因此,假设您的代码如下所示:
if __name__ == "__main__" and __package__ is None:
__package__ = "app.pkg"
from .. import mod
你应该得到这样的例外:
SystemError: Parent module 'app' not loaded, cannot perform relative import
正如错误消息所暗示的那样,您需要以某种方式导入app
,然后才能执行任何相对导入。这显然意味着您无法相对导入app
,因此您需要绝对导入它。有各种各样的hacky方法你可以做到这一点,或者你可以做PEP建议和munge sys.path
所带来的所有问题。但除此之外,你不能这样做。
这只是尝试从程序包中间运行脚本很难的多个原因之一。那是故意的;正如PEP 3122中所述:
Guido将包中的脚本视为反模式。
有两种方法可以达到你想要的效果。第一,你已经开始工作了:只需将模块作为模块而不是脚本运行。 (并且使这项工作成为PEP 366的主要目的。)另一个是将模块和脚本分成两部分。你可以通过写一个包装器来做到这一点。
例如,假设submod.py
看起来像这个愚蠢的玩具示例:
if __name__ == "__main__" and __package__ is None:
__package__ = "app.pkg"
from .. import mod
def main(argv):
print(argv)
if __name__ == '__main__':
import sys
main(sys.argv)
创建一个新文件,例如sub.py
,作为该软件包的兄弟:
import sys
import app.pkg.submod
app.pkg.submod.main(sys.argv)
现在,您可以从系统的任何位置运行sub.py
。正如sys.path
所说:
在程序启动时初始化时,此列表的第一项path [0]是包含用于调用Python解释器的脚本的目录。
由于该目录也是app
所在的目录,因此您可以保证绝对导入app.pkg.submod
,这意味着submod
将能够相对导入不管它想要什么。
答案 1 :(得分:1)
包裹不是pkg2
;它是app.pkg2
。相应地设置__package__
:
if __name__ = '__main__' and __package__ is None:
__package__ = 'app.pkg2'
如果那不是问题,那么app
可能不在路径上。我所知道的最好的解决方案是将它放在路径上(通过移动app
或将其当前目录放在PYTHONPATH中),但如果您正在寻找一种方法让app
中的模块看到每个其他没有外部模块可见,我什么都不知道。看起来它可能很有用。