重写__getattr__代理其他对象方法时如何加类型提示实现IDE补全?
实现动态代理类属性自动补全的两种常用方案
方案1:固定代理单个类,最简实现
如果你的Pizza类固定只代理Topping的属性,直接让Pizza类继承Topping即可,类型检查器会通过继承关系识别到Pizza实例拥有Topping的所有属性和方法,实际运行时还是通过你写的__getattr__转发请求,不会产生冲突:
class Topping: def __init__(self, name: str) -> None: self.name = name def topping_cost(self) -> int: return 3 # 继承Topping仅用于给类型检查器提供提示,不影响运行逻辑 class Pizza(Topping): def __init__(self, topping: Topping) -> None: self.topping = topping def __getattr__(self, attr): if hasattr(self.topping, attr): return getattr(self.topping, attr) raise AttributeError(f'Missing attr {attr!r}')
方案2:通用代理类,支持代理任意类型
如果你的项目有多个需要动态代理的场景,可以用泛型封装通用代理父类,后续所有需要代理功能的类继承该父类即可,复用性更高:
from typing import TypeVar, Generic, Any T = TypeVar('T') # 通用代理父类,泛型参数T为被代理的类类型 class BaseDelegate(Generic[T]): def __init__(self, delegate_obj: T) -> None: self._delegate_obj = delegate_obj def __getattr__(self, attr: str) -> Any: if hasattr(self._delegate_obj, attr): return getattr(self._delegate_obj, attr) raise AttributeError(f'Missing attribute {attr!r}') class Topping: def __init__(self, name: str) -> None: self.name = name def topping_cost(self) -> int: return 3 # 声明代理的目标类型为Topping,IDE会自动识别Topping的所有属性和方法 class Pizza(BaseDelegate[Topping]): def __init__(self, topping: Topping) -> None: super().__init__(topping) # 可自定义Pizza本身的属性和方法 self.size = "12寸" def calc_total_price(self) -> int: # 可以直接调用被代理的Topping方法,IDE会自动补全 return 25 + self.topping_cost()
效果验证
两种方案都可以让你在调用pizza.topping_cost()时获得IDE的自动补全提示,同时mypy、pyright等类型检查工具也不会报错。
内容的提问来源于stack exchange,提问作者kvothe__
相关产品推荐
相关产品推荐

