如何在python中完成相对导入

How to accomplish relative import in python

1
2
3
4
5
6
7
8
9
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

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

1
2
3
4
5
6
stuff/
    mylib.py
    foo.py // equivalent of main.py in above
    foo/
        __init__.py
        script.py

但得到了同样的错误。

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

编辑:在Python 2中


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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中的一行看起来像......

1
from core.object_editor import editor

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

1
from .. import ntlib

或者

1
from core import ntlib

关键是在我给出的问题示例中,"main"脚本是从包中运行的。一旦我把它移出去,创建了一个特定的包(core),并移动了我希望编辑者共享(ntlib)到该包中的库,一切都是hunky-dory。


虽然你的python PATH中没有长的"东西",但你别无选择,只能添加路径。

如果你从你可以做的东西中知道你的script.py的级别,例如:

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


我正在Windows 7上运行Python 3.4.2并且在我身上撕掉我的头发。

运行以下任何一个时:

python -m unittest
python -m unittest discover

...我会得到'尝试相对导入超出顶级包'错误。

对我来说,解决方案是在我的[test_stock.py]中删除".."。
这条线是:
来自..stock进口库存

将其更改为:
从库存进口库存

..而且它有效。

文件夹结构:

1
2
3
4
5
6
7
8
9
10
11
C:\
  |
  +-- stock_alerter
             |
             +-- __init__.py
             +-- stock.py
             |
             \-- tests
                   |
                   +-- __init__.py
                   \-- test_stock.py


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

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

但是,PEP似乎没有考虑将mylib打包在一个模块中。因此,您可能需要更改调用库函数的方式。

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


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

编辑取消了扩展


如果你使用的是Linux或类似的* nix,你可以使用符号链接来解决这个问题。

1
2
3
4
5
6
7
8
9
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导入有深入的了解。

1
2
3
4
5
6
7
8
9
10
11
tests/
    common/
        commonlib.py
    test1/
        executable1.py
        executable2.py
        commonlib.py -> ../common/commonlib.py
    test2/
        executable1.py
        executable2.py
        commonlib.py -> ../common/commonlib.py