26

在 Python 中使用相对导入有一个缺点,您将无法再将模块作为独立模块运行,因为您会遇到异常:ValueError: Attempted relative import in non-package

# /test.py: just a sample file importing foo module
import foo
...

# /foo/foo.py:
from . import bar
...
if __name__ == "__main__":
   pass

# /foo/bar.py: a submodule of foo, used by foo.py
from . import foo
...
if __name__ == "__main__":
   pass

我应该如何修改示例代码以便能够执行 all test.py:foo.pybar.py

我正在寻找适用于 python 2.6+(包括 3.x)的解决方案。

4

6 回答 6

22

您可以以不同的方式开始“将模块作为独立运行”:

代替:

python foo/bar.py

采用:

python -mfoo.bar

当然,foo/__init__.py文件必须存在。

另请注意,您在foo.pyand之间存在循环依赖关系bar.py——这是行不通的。我想这只是你的例子中的一个错误。

更新:似乎它也可以很好地使用它作为第一行foo/bar.py

#!/usr/bin/python -mfoo.bar

然后您可以直接在 POSIX 系统中执行脚本。

于 2010-09-01T12:23:36.463 回答
16

首先,我假设您意识到您所写的内容会导致循环导入问题,因为 foo 导入 bar ,反之亦然;尝试添加

from foo import bar

到 test.py,你会看到它失败了。必须更改示例才能正常工作。

所以,你要问的是当相对导入失败时回退到绝对导入;实际上,如果您将 foo.py 或 bar.py 作为主模块执行,则其他模块将仅位于根级别,并且如果它们与系统上的另一个模块共享名称,则选择哪个模块取决于sys.path 中的顺序。由于当前目录通常是第一个,如果可用,将选择本地模块 - 即,如果您在当前工作目录中有一个“os.py”文件,它将被选择而不是内置的。

一个可能的建议是:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

酒吧.py:

if __name__ == "__main__":
    pass

顺便说一句,从正确的位置调用脚本通常会更好

python -m foo.bar

可能是最好的方法。这会将模块作为脚本运行

于 2010-09-05T22:44:53.027 回答
1

抛弃相对导入:无论如何,您应该将包命名空间视为全局命名空间。

使这个可口的诀窍是sys.path适当地编辑。这里有一些值得深思的地方:

# 上一个目录
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir) 现在
于 2010-09-06T03:00:23.690 回答
1

您需要__init__.py在每个文件夹中。

仅当您执行以下操作时,相对导入才有效:

python test.py

test.py 导入 foo.py 和 foo.py 可以相对导入 test.py 及以上文件夹中的任何内容。

你不能这样做:

cd foo
python foo.py
python bar.py

它永远不会起作用。

您可以尝试 sys.path.append 或 sys.path.insert 解决方案,但您会搞砸路径,并且 f=open(filename) 会出现问题。

于 2011-02-12T07:43:41.600 回答
0

为什么不把“main”放在不同的 .py 文件中?

于 2010-09-06T02:55:52.340 回答
-2

到目前为止,我发现的唯一解决方案是根本不使用相对导入。

由于目前的限制,我想知道什么时候应该有人在 python 中使用相对导入。

在我使用的所有配置中,都sys.path包含当前目录作为第一个参数,所以只需使用import foo而不是from . import foo因为它会做同样的事情。

于 2010-09-01T12:10:23.827 回答