Python 中的循环导入依赖
假设我有以下目录结构:
a\
__init__.py
b\
__init__.py
c\
__init__.py
c_file.py
d\
__init__.py
d_file.py
在 a
包的 __init__.py
中,导入了 c
包。但是c_file.py
导入abd
。
当 c_file.py
尝试导入 abd
时,程序失败,提示 b
不存在。 (它确实不存在,因为我们正在导入它。)
如何解决这个问题?
Let's say I have the following directory structure:
a\
__init__.py
b\
__init__.py
c\
__init__.py
c_file.py
d\
__init__.py
d_file.py
In the a
package's __init__.py
, the c
package is imported. But c_file.py
imports a.b.d
.
The program fails, saying b
doesn't exist when c_file.py
tries to import a.b.d
. (And it really doesn't exist, because we were in the middle of importing it.)
How can this problem be remedied?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您可以推迟导入,例如在
a/__init__.py
中:也就是说,推迟导入直到真正需要它为止。然而,我也会仔细查看我的包定义/使用,因为像指出的那样的循环依赖可能表明存在设计问题。
You may defer the import, for example in
a/__init__.py
:that is, defer the import until it is really needed. However, I would also have a close look at my package definitions/uses, as a cyclic dependency like the one pointed out might indicate a design problem.
如果a依赖于c并且c依赖于a,那么它们实际上不是同一个单位吗?
您应该真正检查为什么将 a 和 c 拆分为两个包,因为您应该将一些代码拆分到另一个包中(以使它们都依赖于该新包,但不依赖于彼此),或者您应该合并它们装入一个包中。
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.
我曾多次想过这个问题(通常是在处理需要相互了解的模型时)。简单的解决方案就是导入整个模块,然后引用您需要的东西。
因此,不要
在一个和
另一个中执行,只需
在其中一个中执行,然后在需要时调用
models.Classroom
。I've wondered this a couple times (usually while dealing with models that need to know about each other). The simple solution is just to import the whole module, then reference the thing that you need.
So instead of doing
in one, and
in the other, just do
in one of them, then call
models.Classroom
when you need it.由于类型提示而产生的循环依赖 通过
类型提示,有更多机会创建循环导入。幸运的是,有一个使用特殊常量的解决方案:
打字。 TYPE_CHECKING
。以下示例定义了一个
Vertex
类和一个Edge
类。边由两个顶点定义,顶点维护其所属的相邻边的列表。没有类型提示,没有错误
文件:vertex.py
文件:edge.py
类型提示原因
ImportError
文件:vertex.py
文件:edge.py
使用 TYPE_CHECKING 的解决方案
文件:vertex.py
文件:edge.py
请注意缺少类型提示周围的引号。由于 Python 3.10 中的注释评估被推迟,这些类型提示是就好像它们在引号中一样对待。
带引号与不带引号的类型提示
在 3.10 之前的 Python 版本中,有条件导入的类型必须用引号引起来,使它们成为“前向引用”,从而在解释器运行时隐藏它们。
在 Python 3.7、3.8 和 3.9 中,解决方法是使用以下特殊导入。
Circular Dependencies due to Type Hints
With type hints, there are more opportunities for creating circular imports. Fortunately, there is a solution using the special constant:
typing.TYPE_CHECKING
.The following example defines a
Vertex
class and anEdge
class. An edge is defined by two vertices and a vertex maintains a list of the adjacent edges to which it belongs.Without Type Hints, No Error
File: vertex.py
File: edge.py
Type Hints Cause
ImportError
File: vertex.py
File: edge.py
Solution using TYPE_CHECKING
File: vertex.py
File: edge.py
Notice the absence of the quotes around the type hints. Because of the postponed evaluation of annotations in Python 3.10, these type hints are treated as if they were in quotes.
Quoted vs. Unquoted Type Hints
In versions of Python prior to 3.10, conditionally imported types must be enclosed in quotes, making them “forward references”, which hides them from the interpreter runtime.
In Python 3.7, 3.8, and 3.9, a workaround is to use the following special import.
问题是,当从目录运行时,默认情况下只有子目录的包作为候选导入可见,因此您无法导入 abd 但可以导入 bd,因为 b 是 a 的子包。
如果您确实想在
c/__init__.py
中导入 abd,可以通过将系统路径更改为 a 上面的一个目录并更改a/__init__.py
中的导入来实现此目的code> 为 import abc您的
a/__init__.py
应该如下所示:当您想要将 c 中的模块作为脚本运行时,会出现另一个困难。这里包a和b不存在。您可以破解c目录中的
__int__.py
,将sys.path指向顶级目录,然后在c内的任何模块中导入__init__
,以便能够使用完整路径导入 abd 我怀疑导入 __init__.py 是个好习惯,但它对我的用例有效。The problem is that when running from a directory, by default only the packages that are sub directories are visible as candidate imports, so you cannot import a.b.d. You can however import b.d. since b is a sub package of a.
If you really want to import a.b.d in
c/__init__.py
you can accomplish this by changing the system path to be one directory above a and change the import ina/__init__.py
to be import a.b.c.Your
a/__init__.py
should look like this:An additional difficulty arises when you want to run modules in c as scripts. Here the packages a and b do not exist. You can hack the
__int__.py
in the c directory to point the sys.path to the top-level directory and then import__init__
in any modules inside c to be able to use the full path to import a.b.d. I doubt that it is good practice to import__init__.py
but it has worked for my use cases.我建议采用以下模式。使用它将使自动完成和类型提示正常工作。
cyclo_import_a.py
cyclo_import_b.py
您不能导入类 A 和类; B 使用这种语法
你不能在 class B __ init __ 方法中声明参数 a 的类型,但你可以这样“强制转换”它:
I suggest the following pattern. Using it will allow auto-completion and type hinting to work properly.
cyclic_import_a.py
cyclic_import_b.py
You cannot import classes A & B using this syntax
You cannot declare the type of parameter a in class B __ init __ method, but you can "cast" it this way:
另一种解决方案是对 d_file 使用代理。
例如,假设您想与 c_file 共享 blah 类。因此,d_file 包含:
这是您在 c_file.py 中输入的内容:
在 a 的 init.py 中:
Another solution is to use a proxy for the d_file.
For example, let's say that you want to share the blah class with the c_file. The d_file thus contains:
Here is what you enter in c_file.py:
And in a's init.py: