如何让Pyright区分类变量与实例变量类型?初始化报错求助
解决Pyright识别类/实例变量不同类型时的初始化报错问题
你的核心问题是:通过描述符的__get__重载实现了类属性为Field[InstanceT]、实例属性为InstanceT的类型区分,但dataclass初始化时传入InstanceT类型值会被Pyright报错,因为字段类型被标注为Field[int],而Literal[1]不属于该类型。
问题根源
你的Field类没有实现__set__方法,属于非数据描述符。当dataclass初始化赋值时,会直接给实例设置同名属性(绕过描述符),但Pyright只会根据字段标注的Field[int]类型做校验,所以会报错。同时你原有的first变量是描述符实例的共享状态,多个实例访问时会出现逻辑错误。
解决方案
给Field添加__set__方法使其成为数据描述符,并用字典存储每个实例的独立值,同时完善类型注解,让Pyright识别初始化时可接受InstanceT类型:
import typing as ty from dataclasses import dataclass class Field[InstanceT]: def __init__(self, default: InstanceT): self.default = default # 用字典存储每个实例的对应值,避免共享状态 self._instance_values: dict[object, InstanceT] = {} def __get__(self, obj: ty.Optional[object], owner: type) -> ty.Union[ty.Self, InstanceT]: if obj is None: # 类访问时返回描述符自身 return self # 实例访问时返回对应值,无则返回默认值 return self._instance_values.get(obj, self.default) def __set__(self, obj: object, value: InstanceT) -> None: # 给实例设置值时存入字典 self._instance_values[obj] = value if ty.TYPE_CHECKING: @ty.overload def __get__(self, obj: None, owner: type) -> ty.Self: ... @ty.overload def __get__(self, obj: object, owner: type) -> InstanceT: ... @dataclass class Model: field: Field[int] = Field(0) if __name__ == "__main__": # 类访问类型仍为Field[int] class_field: Field[int] = Model.field assert isinstance(class_field, Field) # 实例访问类型为int instance = Model(field=1) instance_field: int = instance.field assert isinstance(instance_field, int) assert instance.field == 1 # 修改实例值也能正常识别类型 instance.field = 2 assert instance.field == 2
效果说明
- 类型校验正常:Pyright会识别到
Model初始化时传入int是合法的,因为描述符的__set__方法接受int类型。 - 类/实例类型区分正确:类访问
Model.field时类型为Field[int],实例访问instance.field时类型为int。 - 逻辑修复:用字典存储每个实例的值,避免了原代码中
first变量导致的多实例状态混乱问题。
内容的提问来源于stack exchange,提问作者Alex Filiov
相关产品推荐
相关产品推荐

