现在有项目结构如下,各模块都有一个与模块名同名但首字母大写的类,比如player.py
里有Player
类
project/
│
├── entity/
│ ├── __init__.py
│ ├── player.py
│ └── ball.py
│
└── main.py
# __init__.py
from entity.player import Player
from entity.ball import Ball
# player.py
from entity.ball import Ball
class Player:
def play(self, ball: Ball):
...
# ball.py
from entity.player import Player
class Ball:
def hit(self, player: Player):
...
这时就会出现循环导入的错误
ImportError: cannot import name 'Player' from partially initialized module 'entity.player' (most likely due to a circular import)
可以先看看代码执行顺序
entity/__init__.py
从entity/player.py
导入Player
sys.modules
中添加了entity.player
模块,并执行里面的代码entity/player.py
从entity/ball.py
导入Ball
sys.modules
中添加了entity.ball
模块,并执行里面的代码entity/ball.py
从entity/player.py
导入Player
sys.modules
中已存在entity.player
模块,于是直接从该模块中获取Player
entity/ball.py
创建Ball
类问题在第6步,获取Player
时,entity.player
模块还在执行from entity.ball import Ball
,
而Player
目前还没有被定义,导致无法找到,抛出ImportError
为了验证是否真是这样 创建a.py和b.py
# a.py
import sys
print("fff" in sys.modules["a"].__dict__)
fff = 0
print("fff" in sys.modules["a"].__dict__)
# b.py
import a
执行b.py,输出
False
True
大部分情况下,我们导入一个类在都是用于类型注释,而实际创建时都是在函数内部
因此,有两种方法
如果先导入的是player.py,那就确保player.py中先包含Player类,再导入Ball
# player.py
class Player:
def play(self, ball: 'Ball') -> None:
...
from entity.ball import Ball
注意此时在类型注释中,由于Ball类在下面才定义,Ball的注释需要使用引号包裹
由于typing.TYPE_CHECKING的值固定为False, 所有我们可以在if TYPE_CHECKING下导入只用来类型检查的模块
# player.py
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from entity.ball import Ball
class Player:
def play(self, ball: 'Ball') -> None:
from entity.ball import Ball # 如果要在内部创建Ball,需要引入
注意此时在类型注释中,由于Ball类从未被真正导入,Ball的注释需要使用引号包裹 而如果要在函数内部创建Ball对象,则需要引入Ball