在MyPy中,相同的类型怎么可能不兼容



使用以下示例:

from typing import Callable, Generic, Type, TypeVar
XType = TypeVar('XType', bound=int)

class C(Generic[XType]):
def f(self, x_init: XType) -> XType:
return x_init

def combinator(c_cls: Type[C[XType]]) -> Callable[[C[XType], XType], XType]:
old_f = c_cls.f
def new_f(c: C[XType], x_init: XType) -> XType:
return old_f(c, x_init)
return new_f

MyPy说:

a.py:15: error: Incompatible return value type (got "XType", expected "XType")
a.py:15: error: Argument 1 has incompatible type "C[XType]"; expected "C[XType]"
a.py:15: error: Argument 2 has incompatible type "XType"; expected "XType"

我不确定我是否同意这个问题的前提。

这是3.8 的部分文档字符串

class TypeVar(_Final, _Immutable, _root=True):
"""Type variable.
Usage::
T = TypeVar('T')  # Can be anything
A = TypeVar('A', str, bytes)  # Must be str or bytes
....
def __init__(self, name, *constraints, bound=None,
covariant=False, contravariant=False):
....

现在,如果你只有

ThetaType = TypeVar('ThetaType')
XType = TypeVar('XType')

您是否认为ThetaType的使用应该被视为XType的使用,即使设置了两个不同的typevar?为什么添加bound可选参数会自动将它们折叠在一起?源不会以任何方式强制绑定的存在,或名称旁边的任何参数。

我不认为打字/mypy的工作是在类型声明中推断你的意图,只是检查你的代码声明的类型意图。如果你的意思是它们是相同的,那么只声明1个TypeVar。如果你有实际的理由使用2,那么认为它们相同可能会失去一些语义。

我将添加到bound允许比constraints更大的灵活性,因为它在子类上匹配。假设您已经定义了int的4个子类。Int1(int(、Int2、Int3、Int4……现在您决定对代码进行分区,其中一些代码应该只接受Int1和Int2。Typevariant12可以在某种程度上表达这一点,即使您的子类都匹配bound=int

我支持@JL Peyret的观点,即这是预期行为。

几个额外的想法:

  1. 这不是TypeVar特有的:如果您创建了两个具有不同名称的相同类,如果您可互换地使用它们,mypy将引发错误

  2. 如果我理解你想做什么,你应该使用NewType而不是TypeVar。将CCD_ 8与CCD_;同样的类型会进进出出。它可能是intfloat、&";。

示例:

from typing import Generic, TypeVar
T = TypeVar("T", int, str)

def double(value: T) -> T:
# functional version
return value * 2

class Doubler(Generic[T]):
# class version - not sure why someone would do this,
# it's just an example.
def __init__(self, value: T):
# tip: use dataclasses or attrs instead
self.value = value
@property
def double(self) -> T:
return self.value * 2  # note this works on ints and strings

我认为你要做的实际上是拥有整数(并且只有整数(的类型,但你希望能够独立跟踪它们,以避免意外地切换它们:

from typing import NewType, Tuple
ThetaType = NewType("ThetaType", int)
XType = NewType("XType", int)

def switch_arguments_order(theta: ThetaType, x: XType) -> Tuple[XType, ThetaType]:
# If you accidentally got the wrong order here, mypy would catch it.
# You can also think of a function that only returns one of the two,
# if you accidentally return the wrong one mypy will catch that.
return x, theta

现在,如果您希望任何一段逻辑处理ThetaTypeXtype而不进行区分,那么您可以让它返回int,mypy不会抱怨。


def mypy_passes(x: Xtype) -> int:
return x

def mypy_fails(n: int) -> Xtype:
return n

def mypy_passes_with_wrapping(n: int) -> Xtype:
return Xtype(n)

最新更新