如何在 Python 模块中正确使用相对或绝对导入?

发布于 2024-09-16 11:48:22 字数 540 浏览 10 评论 0 原文

在 Python 中使用相对导入有一个缺点:您将无法再作为独立运行模块,因为您将收到异常:

ValueError:尝试在非包中进行相对导入

代码

# /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

中进行相对导入我应该如何修改示例代码以便能够执行以下所有操作? test.pyfoo.pybar.py

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

Usage of relative imports in Python has one drawback; you will not be able to run the modules as standalones anymore, because you will get an exception:

ValueError: Attempted relative import in non-package

Code

# /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

How should I modify the sample code in order to be able to execute all of the following? test.py, foo.py and bar.py.

I'm looking for a solution that works with Python 2.6+ (including 3.x).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

酒废 2024-09-23 11:48:22

您可以以一种不同的方式开始“以独立方式运行模块”:

而不是:

python foo/bar.py

使用:

python -mfoo.bar

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

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

看来将其用作 foo/bar.py 的第一行也非常有效:

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

然后您可以直接在 POSIX 系统。

You could just start 'to run the modules as standalones' in a bit a different way:

Instead of:

python foo/bar.py

Use:

python -mfoo.bar

Of course, the foo/__init__.py file must be present.

Please also note, that you have a circular dependency between foo.py and bar.py – this won't work. I guess it is just a mistake in your example.

It seems it also works perfectly well to use this as the first line of the foo/bar.py:

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

Then you can execute the script directly in POSIX systems.

蓝天白云 2024-09-23 11:48:22

首先,我假设您意识到您所写的内容会导致循环导入问题,因为 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

bar.py:

if __name__ == "__main__":
    pass

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

python -m foo.bar

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

First, I assume you realize what you've written would lead to a circular import issue, because foo imports bar and viceversa; try adding

from foo import bar

to test.py, and you'll see it fails. The example must be changed in order to work.

So, what you're asking is really to fallback to absolute import when relative import fails; in fact, if you're executing foo.py or bar.py as the main module, the other modules will just lie at the root level, and if they share the name with another module on the system which one will be picked depends on the order in sys.path. Since the current dir is usually the first, local modules will be picked if available - i.e., if you've got an 'os.py' file in the current working dir, it'll be picked instead of the builtin one.

A possibile suggestion is:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

bar.py:

if __name__ == "__main__":
    pass

By the way calling scripts from the proper position is usually way better.

python -m foo.bar

Is probably the best way to go. This runs the module as a script.

终陌 2024-09-23 11:48:22

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

使这个变得可口的技巧是适当地编辑sys.path。这里有一些值得思考的地方:

# one directory up
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir)for now

Ditch relative imports: you should think of your package namespace as a global one, anyway.

The trick to making this palatable is editing sys.path appropriately. Here is some food for thought:

# one directory up
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir)for now
灵芸 2024-09-23 11:48:22

每个文件夹中都需要 __init__.py

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

python test.py

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

你做不到:

cd foo
python foo.py
python bar.py

它永远不会起作用。

您可以尝试 sys.path.appendsys.path.insert 解决方案,但您会搞砸路径,并且 f= 会出现问题打开(文件名)。

You need __init__.py in each folder.

Relative import works only when you do:

python test.py

test.py imports foo.py and foo.py can relative import anything from the folder of test.py and above.

You can't do:

cd foo
python foo.py
python bar.py

It will never work.

You can try the sys.path.append or sys.path.insert solution, but you are going to screw up the paths and you'll have problems with the f=open(filename).

酒废 2024-09-23 11:48:22

只需将“main”放在不同的 .py 文件中即可。

Just put the "main" in a different .py file.

倾城°AllureLove 2024-09-23 11:48:22

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

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

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

So far the only solution I found was not to use relative imports at all.

Due to current limitations, I'm wondering when someone is supposed to use relative imports in Python.

On all configurations that I used the sys.path contained the current directory as the first argument, so just use import foo instead of from . import foo, because it will do the same.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文