Python软件包结构

16
我有一个Python包,其中包含多个子包。
myproject/
  __init__.py
  models/
    __init__.py
    ...
  controllers/
    __init__.py
    ..
  scripts/
    __init__.py
    myscript.py

在myproject.scripts.myscript中,我该如何访问myproject.models?我尝试过

from myproject import models # No module named myproject
import models # No module named models
from .. import models # Attempted relative import in non-package

我以前曾经解决过这个问题,但我总是记不住应该如何完成。对我来说,这并不直观。


你是如何运行 myscript.py 的?能否展示一下你的命令行?这将对回答有所影响。 - Brandon Rhodes
2
Try import myproject.models - John
好的,我找到了一个解决方案:在导入之前加上 sys.path.append(os.path.dirname(os.path.dirname(__file__)))。虽然我希望有更好的解决方案。 - user113397
@adeel:你试过 import myproject.models 吗? - John
好的,那么我想我会给Jean-Paul点个赞。很高兴你搞定了它。 - John
显示剩余3条评论
2个回答

20

这是正确的版本:

from myproject import models

如果出现“ImportError: No module named foo”错误,那是因为您没有设置PYTHONPATH以包括包含myproject/的目录。
我怕其他人会建议一些技巧来避免设置PYTHONPATH。我敦促您忽略它们。这就是为什么PYTHONPATH存在的原因:告诉Python在哪里查找要加载的代码。它很强大,文档合理,并且可移植到许多环境。为了避免设置它而玩的技巧并非以上所述。
显式相对导入即使没有设置PYTHONPATH也可以工作,因为它只需沿着目录层次结构向上走,直到找到正确的位置,而不需要找到顶部然后向下走。但是,在将脚本作为命令行参数传递给python(或等效地,使用#!/usr/bin/python行直接调用)时,它无法工作。这是因为在这两种情况下,它成为进程的__main__模块。从__main__开始没有任何向上走的地方 - 它已经在顶部!如果通过导入该模块来调用脚本中的代码,则没有问题。也就是说,请比较:
python myproject/scripts/myscript.py

to

python -c 'import myproject.scripts.myscript'

您可以利用这一点,不直接执行您的脚本模块,而是创建一个bin/myscript,进行导入并调用主函数:
import myprojects.scripts.myscript
myprojects.scripts.myscript.main()

与Twisted的命令行脚本定义方式相比,如下所示:http://twistedmatrix.com/trac/browser/trunk/bin/twistd

谢谢。你能解释一下为什么“from ..import models”不起作用吗?我以为相对导入可以在不设置PYTHONPATH的情况下实现这一点。 - user113397
1
谢谢,Jean-Paul!我喜欢那种方法。 - user113397

2

你的项目不在你的路径中。

选项A

  • 安装你的包,这样Python就可以通过其绝对名称从任何地方找到它(使用from myproject import models

选项B

  • 通过小技巧将相对父级添加到你的路径中
  • sys.path.append(os.path.abspath('..'))

前者选项是推荐的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接