Python/MyPy:更改泛型 typevar 的方差

问题描述 投票:0回答:0

我正在尝试将类型注释添加到具有链接的类似节点类的库,并且我面临以下问题的更复杂版本。

考虑以下通用函数:

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, Calalble[[U_contra], V_cov]]
    ...

它在四个变量中是通用的,但实际上,

U_cov
U_contra
不是独立的。有没有什么方法可以表达它们需要是相同的类型,但相对于它们的包含类型有不同的差异?也就是说,让类型检查器认为该函数是通用的,如下所示:

def f(x: T_contra) -> tuple[U, Calalble[[U], V_cov]]

那么 U 型中的不匹配会被视为错误?

这是我所追求的(仍然简化的)示例,这次是链接在一起的类,但其他方面的行为与函数类似(输入逆变,输出协变)。 Node 需要在所有四个变量中都是通用的,这样 MyPy 才不会抱怨这个定义,但同样,

T_cov
T_contra
是共同确定的,就像
U_cov
U_contra
一样。方差与参与向上、自我还是向下有关。

from typing import TypeVar, Generic, Type, 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)

# A Node takes a contravariant T_contra and 
# produces a covariant U_cov.  Like a callable, 
# if I require a Node that produces U, then 
# a Node that produces a more specialized 
# type V <: U will do too: Node[_, V] <: Node[_, U].
# Conversely, if I require a Node that operates 
# on type T, then a Node that operates on 
# a more general type S >: T will also work:
# Node[S, _] <: Node[T, _].
class Node(Generic[T_contra, U_cov]):
    def __init__(
        self, 
        # f is generic in the same types as Node
        f: Callable[[T_contra], U_cov],
        # up node has Any -> T_cov := T_contra
        up: Optional[Node[Any, T_cov]] = None
    ) -> None:
        # down node has U_contra := U_cov -> Any
        self.down: Optional[Node[U_contra, Any]] = None  # assigned by downstream node
        self.f = f
        self.up = up
        if not self.up.down:
            self.up.down = self
        else:
            raise ValueError(f"Multiply linked nodes not allowed yet.")
        self.value: Optional[U_cov] = None
        
    def update(x: T_contra) -> None:
        self.value: U_cov = self.f(x)
        self.down.update(self.value)  # <-- U_cov for self becomes contravariant for down (U_cov -> U_contra, which is T_contra relative to down in this class definition)

目的是对这样的事情进行类型检查

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)  # type: Node[int, str]
n2 = Node(func2, n1)  # type: Node[str, str]; func2 and n1 compatability checked
n3 = Node(func3, n2)  # type: Node[str, float]; func3 and n2 compatability is checked

虽然此示例适用于不变的

T
U
,但在更复杂的情况下,方差变得很重要。举一个简单的例子,考虑一个特定的节点通常会将通用变量专门化为
class SomeNode(Node[float | Sequence[float], float]): ...
,因此如果可以表达适当的不变性,其上节点可以是
Node[Any, float | Sequence[float]
类型;否则,对于不变变量,它必须是
Node[float] | Node[Sequence[float]]

python mypy typing variance
© www.soinside.com 2019 - 2024. All rights reserved.