You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:38:11