【发布时间】:2021-11-11 12:46:31
【问题描述】:
我在 python 中使用了一个Generic 类,它的实例很小,但封装了其他类型的实例:
import random
from typing import Generic
from typing import TypeVar
from typing import Union
T = TypeVar("T", covariant=True)
class Wrapper(Generic[T]):
def __init__(self, value: T) -> None:
self._value = value
@property
def unwrap(self) -> T:
return self._value
def test_union_wrapper() -> None:
def wrapper_union() -> Wrapper[Union[str, int]]:
if random.random() >= 0.5:
return Wrapper("foo")
else:
return Wrapper(42)
# mypy will give an error for this line
w_u: Union[Wrapper[str], Wrapper[int]] = wrapper_union()
在上面的代码上运行mypy会导致:
error: Incompatible types in assignment (expression has type "Wrapper[Union[str, int]]", variable has type "Union[Wrapper[str], Wrapper[int]]")
这似乎是合理的,因为
Wrapper[Union[str, int]] ≮:Wrapper[str],和
Wrapper[Union[str, int]]≮:Wrapper[int]
并且,可以在PEP483 中阅读:
Union在其所有参数中的行为都是协变的。实际上,如上所述,Union[t1, t2, ...]是Union[u1, u2, ...]的子类型,如果t1是u1的子类型等等。
但我对此有异议,因为我知道∀w ∈ Wrapper[Union[str, int]],w ∈ Wrapper[str] 或w ∈ Wrapper[int],因此,Wrapper[Union[str, int]] <:>Union[Wrapper[str], Wrapper[int]],无论如何。我想让mypy 承认同样的事实,但我不知道如何。
甚至还有一个使用标准库进行此类识别的示例。如果我用Type 替换Wrapper - 另一个协变Generic - 在上面的代码中,我们得到:
def test_union_type() -> None:
def type_union() -> Type[Union[str, int]]:
if random.random() >= 0.5:
return str
else:
return int
# mypy has no problem with this
w_u: Union[Type[str], Type[int]] = type_union()
这里,mypy 识别出函数返回类型Type[Union[str, int]] 等价于变量类型Union[Type[str], Type[int]]。
因此我有两个问题:
- 我如何告诉类型检查器
Wrapper的行为与Type对Union的行为相同? - 这种与
Union相关的行为是否有名称?如果我们将 Wrapper 和 Unions 视为类型上的函数,我们可以说它们 commute with each other,但不确定在类型理论或 Python 的上下文中正确的术语是什么。
【问题讨论】:
标签: python generics type-hinting mypy python-typing