使用以下示例:
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的观点,即这是预期行为。
几个额外的想法:
-
这不是
TypeVar
特有的:如果您创建了两个具有不同名称的相同类,如果您可互换地使用它们,mypy将引发错误 -
如果我理解你想做什么,你应该使用
NewType
而不是TypeVar
。将CCD_ 8与CCD_;同样的类型会进进出出。它可能是int
、float
、&";。
示例:
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
现在,如果您希望任何一段逻辑处理ThetaType
或Xtype
而不进行区分,那么您可以让它返回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)