关于以下代码:
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
使用不同类型的变量:
T = TypeVar("T")
Tfield = TypeVar("Tfield")
...
class FieldDataCollection:
...
def get_field_value(self, field: Field[Tfield]) -> Tfield:
...
但是 mypy 似乎发现了差异并以同样的方式抱怨:
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
类吗?
这似乎实际上是一个范围问题,
FieldDataCollection
中的类型T未定义,因此解释不同,在这种情况下不是您正在寻找的解释。将 Generic[T]
添加到 FieldDataCollection
可以解决 mypy 版本 1.11.0 中的此问题,因为它确保 __init__
中使用的类型必须与 get_field_value
中使用的类型相同:
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(Generic[T]):
"""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)
希望这有帮助!