python - 尝试避免 Python 循环依赖

标签 python circular-dependency

我有一个测试环境来尝试了解如何使用 import x 语句导入模块,而不是使用 from x import y 来避免 python 循环依赖:

test/
    __init__.py
        testing.py
    a/
        __init__.py
        m_a.py
    b/
        __init__.py
        m_b.py

这些文件包含以下内容:

测试.py:

from a.m_a import A

m_a.py:

import b.m_b
print b.m_b
class A:
    pass

m_b.py:

import a.m_a
print a.m_a
class B:
    pass

有一种情况我无法理解:

如果我从模块 m_a.pym_b.py 中删除打印语句或仅从 m_b.py 中删除打印语句,则可以正常工作,但是如果打印出现在 m_b.py 中,则会抛出以下错误:

File "testing.py", line 1, in <module>
  from a.m_a import A
File "/home/enric/test/a/m_a.py", line 1, in <module>
  import b.m_b
File "/home/enric/test/b/m_b.py", line 3, in <module>
  print a.m_a
AttributeError: 'module' object has no attribute 'm_a'

你有什么想法吗?

最佳答案

它仅在删除打印语句的情况下“起作用”,因为您实际上并没有执行任何依赖于导入的操作。它仍然是一个损坏的循环导入。

要么在调试器中运行它,要么在每行后面添加一个 print 语句,您将看到会发生什么:

  • testing.py:从 a.m_a 导入 A
  • a.m_a:导入b.m_b
  • b.m_b:导入a.m_a
  • b.m_b:打印a.m_a

它显然是在模块完成导入之前尝试访问a.m_a。 (事实上​​,您可以在回溯中看到堆栈上的 a.m_a 的其余部分。)

如果此时转储 sys.modules,您会发现两个名为 aa.m_a 的部分模块,但是如果您dir(a),则那里还没有m_a

据我所知,在 m_a.py 完成评估之前,m_a 不会添加到 a 中,这一事实并不存在记录在 Python 2.7 文档中的任何位置。 (3.x 对导入过程有更完整的规范,但它也是一个非常不同的导入过程。)因此,您不能依赖此失败成功;任何一种实现方式都是完全合法的。 (但它至少在 CPython 和 PyPy 中失败了……)

<小时/>

更一般地说,使用 import foo 而不是 from foo import bar 并不能神奇地解决所有循环导入问题。它只是解决了一类特定的循环导入问题(或者更确切地说,使该类变得毫无意义)。 (我意识到 the FAQ 中有一些关于此的误导性文字。)

<小时/>

有多种技巧可以解决循环导入,同时仍然让您拥有循环顶级依赖项。但实际上,摆脱循环顶级依赖关系几乎总是更简单。

在这个玩具案例中,a.m_a 根本没有理由依赖 b.m_b。如果您需要一些打印 a.m_a 的工具,有比从完全独立的包更好的方法来获取它!

在现实代码中,m_a 中可能有一些 m_b 需要的东西,反之亦然。但通常,您可以将其分为两个级别:m_a 中需要 m_b 的内容,以及 m_a 需要的内容m_b。所以,只需将其分成两个模块即可。这实际上与尝试备份并导入 main 的一堆模块的常见修复相同:将 utilsmain 分开.

如果 m_b 确实需要 m_a 提供某些东西,而该东西也需要 m_b 怎么办?那么,在这种情况下,您可能必须插入一个间接级别。例如,也许您可​​以将 thing-from-m_b 传递到 m_a 的函数/构造函数/任何内容中,这样它就可以将其作为本地参数值而不是作为一个全局性的。 (如果没有更具体的问题,就很难更具体。)

如果最坏的情况发生,并且您无法通过间接删除导入,则必须将导入移开。这可能再次意味着在函数调用中进行导入等(如常见问题解答中在让您失望的段落之后立即解释的那样),或者只是将一些代码移到导入上方,或者各种其他可能性。但请考虑这些最后的解决方案,以解决无法干净设计的问题,而不是您的设计可遵循的路线图。

关于python - 尝试避免 Python 循环依赖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18156500/

相关文章:

c++ - 包括依赖

python - 使用 bool 数据类型的 python 在 ElasticSearch 中添加一个字段?

python - 如何在数据集的所有行上应用正则表达式?

C++ - 循环依赖(在模板化基类中使用子类的内部类型)

c - 这是解决循环 typedef 依赖的正确方法吗?

c++ - C++中依赖类的生命周期?

python - Pylons 导入 Psycopg2 错误

python - 如何在 OpenCV、Python 中为被跟踪的对象绘制路径行进线

python - 如何在 jinja2 中循环 pandas 数据框的行和列?

dependencies - composer 如何处理一个项目中多个包共享的依赖关系?