模块从常规执行的相关文件夹导入

时间:2013-08-19 21:11:27

标签: python python-2.7

说我有以下结构:

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}}。

有没有办法可以:

  • 相对导入,支持常规 python /path/to/submod.py 来电

  • 使用 python /path/to/submod.py 执行我的模块的能力,而不必篡改sys.pathsys.path

2 个答案:

答案 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中的模块看到每个其他没有外部模块可见,我什么都不知道。看起来它可能很有用。