如何为Python中__getattr__方法的返回值添加正确类型注解?
为包装类的__getattr__添加精确类型注解
针对你用包装类转发属性的场景,以下是几种可行的类型注解方案,避免使用object或Any,同时无需手动编写大量重载:
方案1:用Generic和TypeVar绑定被包装类型
通过Generic和TypeVar将包装器与被包装类关联,结合cast让类型检查器识别返回值与被包装实例属性的类型一致性:
from typing import TypeVar, Generic, cast T = TypeVar('T') class Foo: def __init__(self) -> None: self.restaurant: str = "taco bell" self.finger_count: int = 10 self.login_info: dict[str, str] = { "username": "SilentHare", "password": "123passw0rd", } def foo_method(self) -> None: print("This method is also an attribute") print(f"Login info is {self.login_info}") class FooWrapper(Generic[T]): def __init__(self, instance: T) -> None: self.foo_instance: T = instance def __getattr__(self, attr: str) -> T: return cast(T, getattr(self.foo_instance, attr)) # 使用示例 wrapper = FooWrapper(Foo()) wrapper.foo_method() # 类型检查器无报错 reveal_type(wrapper.restaurant) # mypy会推断为str(需开启严格模式)
方案2:自动生成__getattr__重载
利用反射自动为被包装类的所有非私有属性生成重载,无需手动维护:
from typing import overload, Literal import inspect class Foo: def __init__(self) -> None: self.restaurant: str = "taco bell" self.finger_count: int = 10 self.login_info: dict[str, str] = { "username": "SilentHare", "password": "123passw0rd", } def foo_method(self) -> None: print("This method is also an attribute") print(f"Login info is {self.login_info}") class FooWrapper: def __init__(self) -> None: self.foo_instance: Foo = Foo() # 自动生成所有属性的重载 for name, _ in inspect.getmembers(Foo, predicate=lambda x: not inspect.isroutine(x) or inspect.isfunction(x)): if not name.startswith('_'): # 生成针对当前属性的重载 @overload def __getattr__(self, attr: Literal[name]) -> type(getattr(Foo, name)): ... locals()['__getattr__'] = __getattr__ def __getattr__(self, attr: str): if hasattr(self.foo_instance, attr): return getattr(self.foo_instance, attr) raise AttributeError
这段代码会在模块加载时自动遍历Foo的属性,生成对应的重载,类型检查器能精确识别每个属性的类型。
方案3:使用mypy插件扩展类型系统
如果前两种方案无法满足需求,可以编写mypy插件,让类型检查器直接理解__getattr__的转发逻辑。插件可以动态分析被包装类的属性,为包装器添加精确的类型注解,无需手动维护代码。
总结
- 方案1简单易行,适合大多数场景,虽依赖
cast但能避免Any的模糊性; - 方案2通过自动生成重载实现精确类型推断,适合被包装类频繁更新的场景;
- 方案3是最灵活的方案,但需要熟悉mypy的插件开发API。
内容的提问来源于stack exchange,提问作者flakpm
相关产品推荐
相关产品推荐

