Python 中的循环导入依赖[重复]
- 2025-01-10 08:46:00
- admin 原创
- 19
问题描述:
假设我有以下目录结构:
a\n __init__.py
b\n __init__.py
c\n __init__.py
c_file.py
d\n __init__.py
d_file.py
在a
包的中__init__.py
,c
包被导入。但是c_file.py
导入了a.b.d
。
程序失败,在尝试导入b
时说不存在。(它实际上不存在,因为我们正在导入它。)c_file.py
`a.b.d`
如何解决此问题?
解决方案 1:
您可以推迟导入,例如a/__init__.py
:
def my_function():
from a.b.c import Blah
return Blah()
也就是说,将导入推迟到真正需要的时候。但是,我也会仔细查看我的包定义/使用情况,因为像上面指出的那样的循环依赖关系可能表明存在设计问题。
解决方案 2:
如果 a 依赖于 c 并且 c 依赖于 a,那么它们实际上不是同一个单位吗?
您应该真正检查一下为什么将 a 和 c 分成两个包,因为要么您有一些代码应该分成另一个包(以使它们都依赖于新包,但不互相依赖),要么您应该将它们合并到一个包中。
解决方案 3:
我曾多次思考过这个问题(通常是在处理需要相互了解的模型时)。简单的解决方案就是导入整个模块,然后引用您需要的东西。
所以不要这样做
from models import Student
一是
from models import Classroom
另一种方式是
import models
在其中一个中,然后models.Classroom
在需要时调用。
解决方案 4:
由于类型提示导致的循环依赖
有了类型提示,创建循环导入的机会就更多了。幸运的是,有一个使用特殊常量的解决方案:typing.TYPE_CHECKING
。
下面的例子定义了一个Vertex
类和一个Edge
类。一条边由两个顶点定义,一个顶点维护着它所属的相邻边的列表。
没有类型提示,没有错误
文件:vertex.py
class Vertex:
def __init__(self, label):
self.label = label
self.adjacency_list = []
文件:edge.py
class Edge:
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
类型提示原因ImportError
ImportError:无法从部分初始化的模块“edge”导入名称“Edge”(很可能是由于循环导入)
文件:vertex.py
from typing import List
from edge import Edge
class Vertex:
def __init__(self, label: str):
self.label = label
self.adjacency_list: List[Edge] = []
文件:edge.py
from vertex import Vertex
class Edge:
def __init__(self, v1: Vertex, v2: Vertex):
self.v1 = v1
self.v2 = v2
使用 TYPE_CHECKING 的解决方案
文件:vertex.py
from typing import List, TYPE_CHECKING
if TYPE_CHECKING:
from edge import Edge
class Vertex:
def __init__(self, label: str):
self.label = label
self.adjacency_list: List[Edge] = []
文件:edge.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from vertex import Vertex
class Edge:
def __init__(self, v1: Vertex, v2: Vertex):
self.v1 = v1
self.v2 = v2
请注意,类型提示周围没有引号。由于Python 3.10 中注释的评估被推迟,这些类型提示被视为在引号中。
带引号与不带引号的类型提示
在 Python 3.10 之前的版本中,有条件导入的类型必须用引号括起来,使其成为“前向引用”,从而将它们隐藏在解释器运行时中。
在 Python 3.7、3.8 和 3.9 中,一种解决方法是使用以下特殊导入。
from __future__ import annotations
解决方案 5:
问题是,当从目录运行时,默认情况下只有子目录的包才可作为候选导入,因此您无法导入 abd,但是您可以导入 bd,因为 b 是 a 的子包。
如果你确实想导入 abd,c/__init__.py
可以通过将系统路径更改为 a 上方的一个目录并将导入更改a/__init__.py
为 import abc 来实现
你a/__init__.py
看起来应该是这样的:
import sys
import os
# set sytem path to be directory above so that a can be a
# package namespace
DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0,DIRECTORY_SCRIPT+"/..")
import a.b.c
当您想将 c 中的模块作为脚本运行时,会出现另一个困难。这里包 a 和 b 不存在。您可以破解__int__.py
c 目录中的 sys.path 以指向顶级目录,然后导入__init__
c 中的任何模块,以便能够使用完整路径导入 abd 我怀疑导入是否是好的做法,__init__.py
但它对我的用例有效。
解决方案 6:
我建议采用以下模式。使用它将允许自动完成和类型提示正常工作。
循环导入a.py
import playground.cyclic_import_b
class A(object):
def __init__(self):
pass
def print_a(self):
print('a')
if __name__ == '__main__':
a = A()
a.print_a()
b = playground.cyclic_import_b.B(a)
b.print_b()
循环导入b.py
import playground.cyclic_import_a
class B(object):
def __init__(self, a):
self.a: playground.cyclic_import_a.A = a
def print_b(self):
print('b1-----------------')
self.a.print_a()
print('b2-----------------')
不能使用此语法导入类 A 和 B
from playgroud.cyclic_import_a import A
from playground.cyclic_import_b import B
您不能在类 B 的 init 方法中声明参数 a 的类型,但您可以通过以下方式“强制转换”它:
def __init__(self, a):
self.a: playground.cyclic_import_a.A = a
解决方案 7:
另一个解决方案是使用 d_file 的代理。
例如,假设您想与 c_file 共享 blah 类。d_file 因此包含:
class blah:
def __init__(self):
print("blah")
以下是您在 c_file.py 中输入的内容:
# do not import the d_file !
# instead, use a place holder for the proxy of d_file
# it will be set by a's __init__.py after imports are done
d_file = None
def c_blah(): # a function that calls d_file's blah
d_file.blah()
在 a 的init.py中:
from b.c import c_file
from b.d import d_file
class Proxy(object): # module proxy
pass
d_file_proxy = Proxy()
# now you need to explicitly list the class(es) exposed by d_file
d_file_proxy.blah = d_file.blah
# finally, share the proxy with c_file
c_file.d_file = d_file_proxy
# c_file is now able to call d_file.blah
c_file.c_blah()
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件