Python/MyPy:如何让同一类型拥有不同协变/逆变属性?
泛型方差与链接节点类型注解问题
一、泛型函数的类型关联需求
现有如下泛型函数:
T_contra = TypeVar("T_contra", contravariant=True) U_cov = TypeVar("U_cov", covariant=True) U_contra = TypeVar("U_contra", contravariant=True) V_cov = TypeVar("V_cov", covariant=True) def f(x: T_contra) -> tuple[U_cov, Callable[[U_contra], V_cov]]: ...
其中U_cov与U_contra并非独立类型,实际应为同一类型,但在不同位置需要不同方差:作为元组第一个元素时是协变,作为Callable的参数时是逆变。
能否让类型检查器将其视为如下等效泛型形式,同时保留对应位置的方差特性?
def f(x: T_contra) -> tuple[U, Callable[[U], V_cov]]: ...
以此实现对U类型不匹配的报错校验。
二、链接节点类的泛型方差优化
Node类行为类似可调用对象,输入为逆变类型、输出为协变类型:
- 若需要输出U类型的Node,输出更特化类型V(V <: U)的Node也适用,即
Node[_, V] <: Node[_, U] - 若需要处理T类型的Node,处理更通用类型S(S >: T)的Node也适用,即
Node[S, _] <: Node[T, _]
当前为了让MyPy不报错,定义了四个TypeVar,但T_cov与T_contra、U_cov与U_contra实则是关联的同一类型,方差仅取决于它们在up、自身、down中的角色:
from typing import TypeVar, Generic, Optional, Any T_cov = TypeVar("T_cov", contravariant=True) T_contra = TypeVar("T_contra", contravariant=True) U_cov = TypeVar("U_cov", covariant=True) U_contra = TypeVar("U_contra", contravariant=True) class Node(Generic[T_contra, U_cov]): def __init__( self, f: Callable[[T_contra], U_cov], up: Optional[Node[Any, T_cov]] = None ) -> None: self.down: Optional[Node[U_contra, Any]] = None self.f = f self.up = up if self.up and not self.up.down: self.up.down = self elif self.up: raise ValueError(f"Multiply linked nodes not allowed yet.") self.value: Optional[U_cov] = None def update(self, x: T_contra) -> None: self.value: U_cov = self.f(x) if self.down: self.down.update(self.value)
期望的类型检查效果
以下代码应通过类型检查,同时保留方差带来的类型兼容性校验:
def func1(x: int) -> str: return str(x) def func2(x: str) -> str: return f"{x}.{x}" def func3(x: str) -> float: return float(x) n1 = Node(func1) # 类型应为Node[int, str] n2 = Node(func2, n1) # 类型应为Node[str, str]; 校验func2与n1的兼容性 n3 = Node(func3, n2) # 类型应为Node[str, float]; 校验func3与n2的兼容性
虽然用不变量T和U也能运行,但复杂场景下方差的类型校验至关重要,例如特定节点的上游类型匹配需求。
内容的提问来源于stack exchange,提问作者TCM
相关产品推荐
相关产品推荐

