循环依赖-它何时终止? [英] Circular dependency - when does it terminate?

查看:37
本文介绍了循环依赖-它何时终止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解python如何管理 import s.

I am having trouble understanding how python manages imports.

假设我具有以下应用程序结构:

Let's say I have the following application structure:

application/
- application.py
- model/
-- __init__.py
-- user.py

让我们说 application.py 文件在创建db后导入模型模块,如下所示:

Let's say that the application.py file imports the model module after creating the db like so:

db = SQLAlchemy(application) 
import model

我们也可以说 model 模块导入 user.py 文件,如下所示:

Let's also say the the model module imports the user.py file like so:

import user

最后,假设 user.py 文件从 application.py 文件中导入 db 实例,如下所示:

Finally, let's say that the user.py file imports the db instance from the application.py file like so:

from application import db

对我来说,这似乎是一种循环依赖,因为 application.py 文件间接需要 user.py 文件,但需要 user.py 文件需要 application.py 文件中的 db 实例.

This seems like a circular dependency to me as the application.py file indirectly requires the user.py file but the user.py file requires the db instance from the application.py file.

我知道这段代码在我对其进行测试时就可以正常工作,但是有人可以确切解释Python如何处理它以及何时终止循环周期.

I know that this code does work as I tested it but can someone explain exactly how Python handles this, and when it terminates the cyclical cycle.

总而言之,当 user.py 文件从 application.py 文件导入 db 时,在我看来,它也会调用import model 模块,该模块将创建无限循环.

To summarize the problem, when user.py file imports db from the application.py file it seems to me like it would also call the import model module which creates an infinite loop.

推荐答案

圆形导入本身不一定是问题.只有循环的依赖项.您可以通过一些简单的实验来了解如何解决循环导入:

Circular imports themselves are not necessarily a problem. Only circular dependencies are. You can see how the circular import is resolved with some simple experimentation:

mymod1.py

import sys
print("1. from mymod1:", [x for x in sys.modules if x.startswith("mymod")])
import mymod2
print("2. from mymod1:", [x for x in sys.modules if x.startswith("mymod")])

mymod2.py

import sys
print("1. from mymod2:", [x for x in sys.modules if x.startswith("mymod")])
import mymod3
print("2. from mymod2:", [x for x in sys.modules if x.startswith("mymod")])

mymod3.py

import sys
print("1. from mymod3:", [x for x in sys.modules if x.startswith("mymod")])
import mymod1
print("2. from mymod3:", [x for x in sys.modules if x.startswith("mymod")])

现在我们有一个循环 mymod1->mymod2->mymod3->mymod1 .输入REPL并观察会发生什么:

Now we have a cycle mymod1 -> mymod2 -> mymod3 -> mymod1. Enter the REPL and watch what happens:

>>> import mymod1
1. from mymod1: ['mymod1']  # before mymod1 imported mymod2. note mymod1 is already there!
1. from mymod2: ['mymod1', 'mymod2']  # in mymod2 now, before importing mymod3
1. from mymod3: ['mymod1', 'mymod2', 'mymod3']  # before mymod3 imports mymod1
2. from mymod3: ['mymod1', 'mymod2', 'mymod3']  # mymod3 exit
2. from mymod2: ['mymod1', 'mymod2', 'mymod3']  # mymod2 exit
2. from mymod1: ['mymod1', 'mymod3', 'mymod2']  # mymod1 exit

此处的主要见解是,模块实例本身在完成执行之前已经存在于 sys.modules 中.这意味着可以再次导入它并返回现有对象,而无需再次执行所有模块级代码.

The key insight here is that the module instance itself is already present in sys.modules before it has completed execution. That means it can be imported again and it will return the existing object, without needing to execute all the module-level code again.

包的子模块中的组件具有相互依赖性是很自然的.问题主要是在模块作用域的代码实际开始执行 之类的操作时,例如尝试连接到数据库,因此,请避免直接在模块级别编写任何脚本.

It's natural for components within submodules of a package to have interdependencies. Issues mostly arise when module-scoped code starts actually doing things like trying to connect to a db, so try to avoid scripting anything directly in the module level.

循环导入错误的原因是在模块完成初始化之前需要模块名称空间中的某些内容.在这种情况下,模块本身存在,但是您尝试访问的名称可能还不存在.

The cause of circular import errors are when something in the module namespace is needed before the module has finished initializing. In this case, the module itself exists but the name you're trying to access might not be there yet.

# mymod.py
import sys
print("var" in vars(sys.modules["mymod"]))
var = "I'm a name in mymod namespace"
print("var" in vars(sys.modules["mymod"]))

导入 mymod 会先显示 False ,然后显示 True ,由于导入在执行过程中,模块的名称空间本身仍在更改

Importing mymod would print False and then True, the module's namespace itself is still being mutated as the import is in the process of executing.

一个精明的读者可能已经注意到 mymod2 mymod3 在输出中切换了位置:

An astute reader may have noticed that mymod2 and mymod3 switched places in the output:

2. from mymod2: ['mymod1', 'mymod2', 'mymod3']
2. from mymod1: ['mymod1', 'mymod3', 'mymod2']
                           |_____ wtf? _____|

这实际上不是偶然的!作为加载模块的最后一步,导入机制将实际模块从 sys.modules 中拉出.如果您在REPL中再次检查,则 mymod1 现在将排在最后.

This is actually no accident! The import machinery, as a final step in loading an module, pulls the actual module out of sys.modules. If you check again within the REPL, mymod1 will now be last.

>>> import mymod1
...
>>> [x for x in sys.modules if x.startswith("mymod")]
['mymod3', 'mymod2', 'mymod1']

我不会描述为什么导入系统会这样做,因为它与问题无关,但是有兴趣知道原因的用户应该参阅

I won't describe why the import system does that, because it's not really relevant to the question, but users interested to know the reasons should see this mailing list post from Guido.

这篇关于循环依赖-它何时终止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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