尝试避免Python循环依赖的实验 [英] Experiment trying to avoid Python circular dependencies

查看:117
本文介绍了尝试避免Python循环依赖的实验的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个测试环境,试图了解如何避免使用 import x 语句而不是使用从x导入y :

I have a testing environment to try to understand how python circular dependencies can be avoided importing the modules with an import x statement, instead of using a from x import y:

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

文件具有以下内容:

testing.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

有一种情况我无法理解:

There is a situation which I can't understand:

如果我从modu中删除了打印语句les m_a.py m_b.py 或仅来自 m_b.py 可以,但是如果打印内容位于 m_b.py ,则抛出以下错误:

If I remove the print statements from modules m_a.py and m_b.py or only from m_b.py this works OK, but if the print is present at m_b.py, then the following error is thrown:

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'

您有任何想法吗?

推荐答案

它只能在删除打印语句的情况下起作用,因为您实际上并没有做任何依赖进口的事情。

It only "works" with the print statements removed because you're not actually doing anything that depends on the imports. It's still a broken circular import.

要么在调试器中运行它,要么在每行之后添加 print 语句。 ,然后您会看到会发生什么:

Either run this in the debugger, or add a print statement after each line, and you'll see what happens:


  • testing.py:来自a.m_a导入A

  • a.m_a: import b.m_b

  • b.m_b :导入a.m_a

  • b.m_b:打印a.m_a

  • testing.py: from a.m_a import A
  • a.m_a: import b.m_b
  • b.m_b: import a.m_a
  • b.m_b: print a.m_a

很显然,在模块完成导入之前,它试图访问 a.m_a 。 (实际上,您可以在回溯中查看堆栈中其余的 a.m_a 。)

It's clearly trying to access a.m_a before the module finished importing. (In fact, you can see the rest of a.m_a on the stack in your backtrace.)

如果您现在转储 sys.modules 时,会发现两个名为 a 和<$ c $的部分模块c> a.m_a ,但是如果您 dir(a),则没有 m_a

If you dump out sys.modules at this point, you'll find two partial modules named a and a.m_a, but if you dir(a), there's no m_a there yet.

据我所知, m_a 不会添加到 a 直到 m_a.py 完成评估为止,Python 2.7文档中没有任何记录。 (3.x对导入过程进行了更为完整的规范,但它也是一个完全不同的导入过程。)因此,您不能依靠失败的成功;任何一种实施都是完全合法的。 (但是它至少在CPython和PyPy中碰巧失败了……)

As far as I can tell, the fact that m_a doesn't get added to a until m_a.py finishes evaluating is not documented anywhere in the Python 2.7 documentation. (3.x has much a more complete specification of the import process—but it's also a very different import process.) So, you can't rely on this either failing or succeeding; either one is perfectly legal for an implementation. (But it happens to fail in at least CPython and PyPy…)

更一般地,使用 import foo 而不是foo import bar 不能神奇地解决所有循环导入问题。它只是解决了一类特定的循环导入问题(或者说,解决了该类问题)。 (我意识到常见问题解答。)

More generally, using import foo instead of from foo import bar doesn't magically solve all circular-import problems. It just solves one particular class of circular-import problems (or, rather, makes that class moot). (I realize there is some misleading text in the FAQ about this.)

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

There are various tricks to work around circular imports while still letting you have circular top-level dependencies. But really, it's almost always simpler to get rid of the circular top-level dependencies.

在这种玩具情况下,实际上没有理由使用 a .m_a 完全依赖 b.m_b 。如果您需要一些打印出 a.m_a 的东西,则有比从完全独立的软件包中获得更好的方法了!

In this toy case, there's really no reason for a.m_a to depend on b.m_b at all. If you need some that prints out a.m_a, there are better ways to get it than from a completely independent package!

在现实生活中的代码中, m_a 中可能有些东西, m_b 需要,反之亦然。但通常,您可以将其分为两个级别:需要 m_b m_a 中的内容,以及<$ m_b 所需的c $ c> m_a 。因此,只需将其分为两个模块。这与一堆试图备份和 import main 的模块的通用修补程序确实是同一回事:拆分 utils 关闭 main

In real-life code, there probably is some stuff in m_a that m_b needs and vice-versa. But usually, you can separate it out into two levels: stuff in m_a that needs m_b, and stuff in m_a that's needed by m_b. So, just split it into two modules. It's really the same thing as the common fix for a bunch of modules that try to reach back up and import main: split a utils off main.

如果确实存在 m_b 需要来自 m_a ,还需要 m_b 吗?好吧,在这种情况下,您可能必须插入一个间接级别。例如,也许您可​​以将<-c>- m_b m_a 传递到函数/构造函数/任何对象,因此它可以作为局部参数值而不是全局参数来访问它。 (如果没有更具体的问题,很难更具体地说明问题。)

What if there really is something that m_b needs from m_a, that also needs m_b? Well, in that case, you may have to insert a level of indirection. For example, maybe you can pass the thing-from-m_b into the function/constructor/whatever from m_a, so it can access it as a local parameter value instead of as a global. (It's hard to be more specific without a more specific problem.)

如果情况变得更糟,并且您无法通过间接删除导入,则必须移动导入方式。这可能再次意味着在函数调用等内部进行导入(如在引爆您的段落之后的FAQ中所述),或者仅将一些代码移到导入上方,或者进行其他各种操作。但是,请考虑将这些最后的解决方案用于无法整洁设计的东西,而不是您的设计遵循的路线图。

If worst comes to worst, and you can't remove the import via indirection, you have to move the import out of the way. That may again mean doing an import inside a function call, etc. (as explained in the FAQ immediately after the paragraph that set you off), or just moving some code above the import, or all kinds of other possibilities. But consider these last-ditch solutions to something which just can't be designed cleanly, not a roadmap to follow for your designs.

这篇关于尝试避免Python循环依赖的实验的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆