如何在python中完成相对导入

Posted

技术标签:

【中文标题】如何在python中完成相对导入【英文标题】:How to accomplish relative import in python 【发布时间】:2011-06-07 00:46:30 【问题描述】:
stuff/
    __init__.py
    mylib.py
    Foo/
        __init__.py
        main.py
        foo/
            __init__.py
            script.py

script.py要导入mylib.py

这只是一个示例,但实际上我只想在父目录中进行模块的相对导入。我尝试了各种方法并收到此错误...

Attempted relative import beyond toplevel package

我在某处读到程序启动的脚本不应该在包中,我尝试像这样修改结构......

stuff/
    mylib.py
    foo.py // equivalent of main.py in above
    foo/
        __init__.py
        script.py

但遇到同样的错误。

我怎样才能做到这一点?这甚至是一种适当的方法吗?

编辑:在 Python 2 中

【问题讨论】:

【参考方案1】:

如果您使用的是 Linux 或类似的 *nix,您可以使用符号链接破解它。

stuff/
    mylib.py
    foo.py // equivalent of main.py in above
    foo/
        script.py
        mylib.py  ->  ../mylib.py
    foo2/
        script2.py
        mylib.py  ->  ../mylib.py

这可能不是一个好的模式。

在我的情况下,我选择了它,因为我有多个依赖于同一个库的可执行文件,需要放入单独的目录中。

新的可执行测试的实现不应该要求测试编写者对 python 导入有深入的了解。

tests/
    common/
        commonlib.py
    test1/
        executable1.py
        executable2.py
        commonlib.py -> ../common/commonlib.py
    test2/
        executable1.py
        executable2.py
        commonlib.py -> ../common/commonlib.py


【讨论】:

【参考方案2】:

我在 Windows 7 上运行 Python 3.4.2 并为此烦恼。

当运行其中任何一个时:

python -m 单元测试 python -m unittest 发现

...我会收到“尝试相对导入超出***包”错误。

对我来说,解决方案是在我的 [test_stock.py] 中删除“..”。 该行是: 从 ..stock 导入库存

改为: 从库存进口库存

.. 它有效。

文件夹结构:

C:\
  |
  +-- stock_alerter
             |
             +-- __init__.py
             +-- stock.py
             |
             \-- tests
                   |
                   +-- __init__.py
                   \-- test_stock.py

【讨论】:

您的解决方案有效,但我不明白为什么......另外,Pycharm 对此感到疯狂并以红色突出显示声明 它之所以有效,是因为当您删除 .. 时,它不再是相对导入。相反,它是绝对的进口。 PyCharm 将使用项目的源目录来解析它,这意味着您可能需要在项目设置中手动添加一个目录来帮助它解析导入。【参考方案3】:

在稍微摆弄之后,我意识到如何设置它,为了具体起见,我不会使用 foo bar 名称。我的项目目录设置为...

tools/
    core/
        object_editor/
            # files that need to use ntlib.py
            editor.py # see example at bottom
            __init__.py
        state_editor/
            # files that need to use ntlib.py
            __init__.py
        ntlib.py
        __init__.py # core is the top level package
    LICENSE
    state_editor.py # equivalent to main.py for the state editor
    object_editor.py # equivalent to main.py for the object editor

object_editor.py 中的一行看起来像...

from core.object_editor import editor

editor.py 中的一行看起来像...

from .. import ntlib

或者

from core import ntlib

关键是在我在问题中给出的示例中,“主”脚本是从包中运行的。一旦我将它移出,创建了一个特定的包 (core),并将我希望编辑器共享的库 (ntlib) 移到该包中,一切都变得很简单。

【讨论】:

你明白了。发生的事情是您不能使用从命令行运行的脚本中的相对导入,因此它应该位于组织的顶层,指的是它下面的内容。 为什么从包中运行“主”脚本会导致问题?【参考方案4】:

从PEP看来,您不能使用相对导入来导入未打包的文件。

因此,您需要添加 __init__.py 并将您的导入更改为 from .mylib import * 之类的内容

然而,PEP 似乎不允许将 mylib 打包在一个模块中。因此,您可能需要更改调用库函数的方式。

另一种选择是将 mylib 移动到子包中并将其导入为from .libpackage import mylib

【讨论】:

【参考方案5】:

尽管只要“stuff”不在您的 python PATH 中,您就别无选择,只能添加路径。

如果您知道您的 script.py 的级别,您可以执行以下操作:

import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))

【讨论】:

这是否记录在某处?不是我不相信你,只是想说“从2个目录上导入”需要修改系统路径,我得亲眼看看。 这是我通常最终会做的事情,如果只是出于恶意。 我不会说它完全像这样记录。虽然我自己多次使用它。它只会修改您正在运行的 python 脚本的系统路径,而不是在系统中全局修改。我想你不应该介意。 :P 效果很好,尤其是在单元测试中,您无法更改布局。 env PYTHONPATH=.. python myfile.py 也是另一种方式【参考方案6】:

import ..foo..stuff.mylib应该没问题

EDIT 取消了扩展

【讨论】:

我认为这不是有效的语法。 from ..foo..stuff.mylib import whatever 应该没问题
import sys sys.path.append() import Bar
我不想追加到路径,这就是我做相对导入的原因。 语法仍然无效。正如this PEP 中非常清楚地指出的那样。

以上是关于如何在python中完成相对导入的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python3 中使用带有 if __name__='__main__' 块的相对导入?

如何在python3中正确导入同一目录下的模块

尝试相对导入超出***包 python

python如何实现相对导入

如何在python3中正确导入同一目录下的模块

如何导入上面目录中的 Python 类?