python 3中的相对导入问题

Relative import problems in Python 3

python imports让我抓狂(我对python imports的经验有时根本不符合"explicit is better than implicit":():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[app]
    start.py
        from package1 import module1
    [package1]
        __init__.py
            print('Init package1')
        module1.py
            print('Init package1.module1')
            from . import module2
        module2.py
            print('Init package1.module2')
            import sys, pprint
            pprint.pprint(sys.modules)
            from . import module1

我得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vic@ubuntu:~/Desktop/app2$ python3 start.py
Init package1
Init package1.module1
Init package1.module2
{'__main__': <module '__main__' from 'start.py'>,
 ...
 'package1': <module 'package1' from '/home/vic/Desktop/app2/package1/__init__.py'>,
 'package1.module1': <module 'package1.module1' from '/home/vic/Desktop/app2/package1/module1.py'>,
 'package1.module2': <module 'package1.module2' from '/home/vic/Desktop/app2/package1/module2.py'>,
 ...
Traceback (most recent call last):
  File"start.py", line 3, in <module>
    from package1 import module1
  File"/home/vic/Desktop/app2/package1/module1.py", line 3, in <module>
    from . import module2
  File"/home/vic/Desktop/app2/package1/module2.py", line 5, in <module>
    from . import module1
ImportError: cannot import name module1
vic@ubuntu:~/Desktop/app2$

import package1.module1可以,但我想使用from . import module1,因为我想让package1可移植到我的其他应用程序中,所以我想使用相对路径。

我正在使用python 3。

我需要循环进口。module1中的函数断言其参数之一是module2和viceversa中定义的类的实例。

换言之:

sys.modules包含'package1.module1': 。我想以from . import module1的形式引用它,但它试图得到一个名称,而不是像import package1.module1那样的包(它工作得很好)。我试过import .module1 as m1,但那是语法错误。

另外,module1中的from . import module2工作正常,但module2中的from . import module1不工作……

更新:

这项黑客工作(但我正在寻找"官方"方式):

1
2
3
4
5
6
7
8
print('Init package1.module2')

import sys, pprint
pprint.pprint(sys.modules)

#from . import module1
parent_module_name = __name__.rpartition('.')[0]
module1 = sys.modules[parent_module_name + '.module1']


对于您的问题,一个更好的解决方案是将package1放入它自己的单独包中。当然,它不能导入包2,但是如果它是可重用的,为什么会这样呢?


一般来说,应该避免循环导入,另请参阅相关问题的答案或effbot.org上的这篇文章。

在这种情况下,问题是您导入了from .,其中.是当前包。所以你所有的from . import X进口产品都要经过这个包的__init__.py

如果您在__init__.py中明确地导入模块并给它们取另一个名称(当然,还可以调整其他导入以使用这些名称),则可以使问题更加明显:

1
2
3
print('Init package1')
from . import module1 as m1
from . import module2 as m2

现在,当您在start.py中导入m1时,包首先初始化m1并进入from . import m2行。此时,__init__.py中没有已知的m2,因此会出现导入错误。如果你把__init__.py中的import语句转换过来(所以你先加载m2,然后在m2中,它会发现from . import m1行失败,原因与以前相同。

如果您不明确地导入__init__.py中的模块,后台仍会发生类似的情况。不同的是,您得到了一个不太平坦的结构(因为导入不再只从包开始)。因此,module1module2都得到了"启动",您得到了各自的初始化打印。

为了使其有效,您可以在module2中进行绝对导入。这样您就可以避免包首先需要解决所有问题,并使它重用从start.py导入的内容(因为它具有相同的导入路径)。

或者更好的是,你完全摆脱了循环导入。如果您有循环引用,这通常意味着您的应用程序结构不太好。

(我希望我的解释有意义,我已经很难写了,但总的来说应该是清楚的,我希望…)

编辑

为了响应您的更新,您要做的是使用完整的包名称来获取对模块的引用。这相当于(但要复杂得多)使其工作的第一个可能选项;使用绝对导入时使用的导入路径与start.py中相同。


您的更新模拟了绝对导入的作用:如果您在导入module1时执行此操作,则会模拟import package1.module1。如果要使用动态父包名称,则要在module2.py中导入module1

1
2
import importlib
module1 = importlib.import_module('.module1', __package__)

I need circular imports. A function in module1 asserts that one of its
parameter is instance of a class defined in module2 and viceversa.

如果不想使用绝对导入,可以将类移到单独的模块中,以解决循环依赖关系,或者在函数级别进行导入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.
├── start.py
#       from package1 import module1
└── package1
 &nbsp;&nbsp; ├── __init__.py
#           print("Init package1")
#           from . import module1, module2
    ├── c1.py
#           print("Init package1.c1")
#           class C1:
#               pass
 &nbsp;&nbsp; ├── module1.py
#           print("Init package1.module1")
#           from .c1 import C1
#           from .module2 import C2
 &nbsp;&nbsp; └── module2.py
#           print("Init package1.module2")
#           from .c1 import C1
#           class C2:
#               pass
#           def f():
#               from .module1 import C1

产量

1
2
3
4
Init package1
Init package1.module1
Init package1.c1
Init package1.module2

另一个比重构c1.py更简单的选择是将module{1,2}.py合并为单个common.py。在这种情况下,module{1,2}.pycommon进口。


1
2
module2.py
          import module1

也工作。


确保您的package1是一个文件夹。在__init__.py中创建一个类,比如class1。将逻辑包含在class1下的方法中,比如说method1

现在,编写以下代码-

1
2
from .package1 import class1
class1.method1()

这是我解决问题的方法。综上所述,您的根目录是.,所以使用.符号(例如from .packagefrom app.package来编写您的import语句。


我今天也遇到了同样的问题,在python3.4中,这个问题似乎确实被打破了,但在python3.5中是有效的。

变更日志包含一个条目:

Circular imports involving relative imports are now supported. (Contributed by Brett Cannon and Antoine Pitrou in bpo-17636).

通过BugReport,看起来这并不是一个修复的buf,而是导入工作方式中的一个新特性。参考上文Poke的回答,他指出from . import foo意味着加载__init__.py,并从中获取foo(可能来自隐含加载的子模块列表)。由于python3.5,from . import foo也会这样做,但如果foo不能作为属性使用,它将返回到加载模块列表(sys.modules中查看是否已经存在,从而修复了这个特定的情况。不过,我不能百分之百确定我是否正确地介绍了这项工作。


Python中对循环导入依赖性的公认答案很重要:

If a depends on c and c depends on a, aren't they actually the same unit then?

You should really examine why you have split a and c into two packages, because either you have some code you should split off into another package (to make them both depend on that new package, but not each other), or you should merge them into one package.
— Lasse V. Karlsen?

也许您应该考虑将它们放在同一个模块中。:)