在以下代码中:
from typing import Generic
from typing import TypeVar
from typing import reveal_type
T = TypeVar('T')
class Field(Generic[T]):
"""A field definition with a default value."""
def __init__(self, default_value: T):
self.default_value = default_value
class FieldDataCollection:
"""A collection of field values."""
def __init__(self, value_per_field: dict[Field[T], T]) -> None:
self._value_per_field = value_per_field
def get_field_value(self, field: Field[T]) -> T:
"""Return the field value if in the collection or the field default value."""
return self._value_per_field.get(field, field.default_value)
if __name__ == '__main__':
foo = Field(1)
value = FieldDataCollection({foo: 2}).get_field_value(foo)
reveal_type(value)
在严格模式下运行 1.11.0(但不是以前的版本)时,我从 mypy 中得到以下错误:
error: Incompatible return value type (got "T@__init__", expected "T@get_field_value") [return-value]
error: Argument 1 to "get" of "dict" has incompatible type "Field[T@get_field_value]"; expected "Field[T@__init__]" [arg-type]
error: Argument 2 to "get" of "dict" has incompatible type "T@get_field_value"; expected "T@__init__" [arg-type]
note: Revealed type is "builtins.int"
据我对这些错误的理解,mypy 抱怨在 | 中推断的类型||方法与
__init__
方法中的类型有些不同。
get_field_value
我尝试为
使用不同类型的变量:
get_field_value
但是mypy似乎捕捉到了差异并以相同的方式抱怨:
T = TypeVar("T")
Tfield = TypeVar("Tfield")
...
class FieldDataCollection:
...
def get_field_value(self, field: Field[Tfield]) -> Tfield:
...
我尝试使用
error: Incompatible return value type (got "T", expected "Tfield") [return-value]
error: Argument 1 to "get" of "dict" has incompatible type "Field[Tfield]"; expected "Field[T]" [arg-type]
error: Argument 2 to "get" of "dict" has incompatible type "Tfield"; expected "T" [arg-type]
note: Revealed type is "builtins.int"
或
Mapping
代替
MutableMapping
,效果很好,但是我不能使用
dict
之类的方法
dict
copy
你认为这是mypy方面的回归吗?
否则,您知道如何正确注释
类吗?
FieldDataCollection
class properly?
遇到的问题是 mypy 正确地标识了代码中的一个类型不一致问题,尽管此问题可能很微妙,并且在早期版本中未被发现。
问题分解:
-
类型变量范围: 在
FieldDataCollection
的__init__
方法中,使用dict[Field[T], T]
。这里的T
是一个类型变量,它的作用域仅限于该方法。这意味着每次实例化FieldDataCollection
时,T
都可以代表不同的类型。 -
不一致的使用: 在
get_field_value
方法中,再次使用T
,但它指的是在__init__
中定义的T
的 相同 实例。然而,没有理由相信传递给get_field_value
的Field[T]
与用于实例化FieldDataCollection
的Field[T]
使用相同的T
。
Mypy 的抱怨是有效的:
Mypy 在 1.11.0 版本中加强了对类型变量范围的检查。它现在正确地识别出
__init__
和
get_field_value
中的
T
可能代表不同的类型,因此它标记了这些不一致性。
解决方案:
为了解决这个问题,需要确保
T
在整个
FieldDataCollection
类中表示相同的类型。可以通过使
FieldDataCollection
本身成为泛型来实现:
from typing import Generic, TypeVar
T = TypeVar('T')
class Field(Generic[T]):
def __init__(self, default_value: T):
self.default_value = default_value
class FieldDataCollection(Generic[T]):
def __init__(self, value_per_field: dict[Field[T], T]) -> None:
self._value_per_field = value_per_field
def get_field_value(self, field: Field[T]) -> T:
return self._value_per_field.get(field, field.default_value)
if __name__ == '__main__':
foo = Field(1)
value = FieldDataCollection({foo: 2}).get_field_value(foo)
reveal_type(value) # note: Revealed type is "builtins.int"
解释:
通过使
FieldDataCollection
成为泛型,现在可以确保
T
在整个类的范围内表示相同的类型。这意味着传递给
__init__
和
get_field_value
的
Field
实例必须具有相同的类型参数。
结论:
这不是 mypy 的回归,而是在类型检查方面的一项改进。通过理解类型变量的范围和正确使用泛型,可以解决这个问题并提高代码的类型安全性。
标签:python,annotations,python-typing,mypy From: 78790692