非泛型类方法中Dict类型变量的mypy类型错误及注解方案
问题:mypy 1.11.0严格模式下的类型注解错误
代码示例
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)
错误信息
在启用严格模式的mypy 1.11.0版本(旧版本无此问题)中运行时,出现以下错误:
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"
尝试为get_field_value使用不同的类型变量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等方法。
解答
1. 这不是mypy的回归问题
mypy 1.11.0严格模式下对类型变量的作用域检查变得更严格了。之前的版本可能允许非泛型类的不同方法使用同名类型变量并隐式关联,但新版本要求明确绑定类型变量的作用域,避免潜在的类型不一致风险。
2. 单一类型Field场景的正确注解
将FieldDataCollection定义为泛型类,让整个类共享同一个类型变量T,这样__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 # 将FieldDataCollection改为泛型类,绑定类型变量T 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)
修改后,mypy严格模式下不会再报错,同时你依然可以使用dict的所有方法(比如copy)。
3. 多类型Field场景的注解方案
如果你的FieldDataCollection需要同时存储不同类型的Field(比如Field[int]和Field[str]),可以使用绑定到Field的泛型变量来确保类型匹配:
from typing import Generic, TypeVar, reveal_type T = TypeVar('T') # 定义绑定到Field的类型变量 F = TypeVar('F', bound=Field) class Field(Generic[T]): def __init__(self, default_value: T): self.default_value = default_value class FieldDataCollection(Generic[F, T]): def __init__(self, value_per_field: dict[F, T]) -> None: self._value_per_field = value_per_field def get_field_value(self, field: F) -> T: return self._value_per_field.get(field, field.default_value)
这种方式能确保每个Field的类型和对应值的类型严格匹配,同时保留dict的方法支持。
内容的提问来源于stack exchange,提问作者PoloB
相关产品推荐
相关产品推荐

