如何让mypy识别泛型代理类仅拥有被代理类的属性?
让mypy识别泛型代理类的被代理属性
要解决mypy因__getattr__允许任意属性访问的问题,核心是让mypy明确代理类的属性完全继承自泛型参数C(被代理类)。以下是几种可行的泛型解决方案:
方案一:泛型__getattr__返回类型变量
通过给__getattr__返回一个类型变量,并配合类型断言,让mypy自动推断属性是否属于被代理类:
import typing as t from dataclasses import dataclass C = t.TypeVar('C') T = t.TypeVar('T') class Proxy(t.Generic[C]): def __init__(self, obj: C): self.obj = obj def __getattr__(self, name: str) -> T: # 用cast告知mypy返回值为被代理对象对应属性的类型 return t.cast(T, getattr(self.obj, name)) @dataclass class Data: foo: int pd = Proxy(Data(42)) pd.foo # 类型检查通过,mypy识别为int类型 pd.oof # 类型检查报错:"Proxy[Data]" has no attribute "oof" [attr-defined]
优点:通用泛型方案,类型安全,运行时无额外开销,mypy能准确推断属性类型并拦截不存在的属性。
方案二:重载__class_getitem__模拟被代理类型
通过重载类的__class_getitem__方法,让mypy将Proxy[C]直接视为C类型,复用被代理类的属性检查逻辑:
import typing as t from dataclasses import dataclass C = t.TypeVar('C') class Proxy(t.Generic[C]): def __init__(self, obj: C): self.obj = obj def __getattr__(self, name: str) -> t.Any: return getattr(self.obj, name) @classmethod def __class_getitem__(cls, item: t.Type[C]) -> t.Type[C]: # 让mypy认为Proxy[C]等价于C类型 return item @dataclass class Data: foo: int pd = Proxy(Data(42)) pd.foo # 类型检查通过 pd.oof # 类型检查报错:"Data" has no attribute "oof" [attr-defined]
说明:这是类型检查层面的小技巧,运行时pd仍是Proxy实例,但mypy会将其当作C实例处理,适合快速适配现有代码。
方案三:结合Protocol约束被代理类
如果需要限制被代理类必须具备特定属性,可以定义Protocol并绑定泛型参数:
import typing as t from typing_extensions import Protocol from dataclasses import dataclass class HasFoo(Protocol): foo: int C = t.TypeVar('C', bound=HasFoo) class Proxy(t.Generic[C]): def __init__(self, obj: C): self.obj = obj def __getattr__(self, name: str) -> t.Any: return getattr(self.obj, name) @dataclass class Data(HasFoo): foo: int pd = Proxy(Data(42)) pd.foo # 类型检查通过 pd.oof # 类型检查报错:"Proxy[Data]" has no attribute "oof" [attr-defined]
优点:类型检查更严格,适合需要约束被代理类属性的场景;缺点是灵活性稍差,无法代理任意类。
内容的提问来源于stack exchange,提问作者volferine
相关产品推荐
相关产品推荐

