类型提示导致的循环依赖
有了类型提示,就有更多机会创建循环导入。幸运的是,有一个使用特殊常量的解决方案: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
引用与未引用的类型提示
在 3.10 之前的 Python 版本中,有条件导入的类型必须用引号括起来,使它们成为“前向引用”,从而将它们从解释器运行时隐藏起来。
在 Python 3.7、3.8 和 3.9 中,一种解决方法是使用以下特殊导入。
from __future__ import annotations
这允许使用不带引号的类型提示和条件导入。
在 Python 3.10 中,函数和变量注释将不再在定义时进行评估。相反,字符串形式将保留在相应的注释字典中。静态类型检查器不会看到行为差异,而在运行时使用注释的工具将不得不执行延迟评估。
字符串形式是在编译步骤期间从 AST 获得的,这意味着字符串形式可能不会保留源的确切格式。注意:如果一个注解已经是一个字符串文字,它仍然会被包裹在一个字符串中。