Python如何自动将父类函数调用转发给内部子对象(非继承)
解决方案:动态添加转发方法(保留IDE代码提示)
要实现父类自动转发方法给内部子对象,同时保留IDE的代码提示,最符合Python风格的方式是在类定义阶段动态生成转发方法,而非依赖__getattr__(会丢失提示)。以下是具体实现:
方法1:使用类装饰器自动绑定转发方法
通过装饰器遍历子对象类的公共方法,为父类生成对应的转发函数,并复制原方法的元信息(如参数注解、文档字符串),确保IDE能识别这些方法。
完整代码示例
def forward_methods(to_attr: str): """ 类装饰器:将指定子对象的公共方法转发到父类 :param to_attr: 父类中子对象的属性名 """ def decorator(cls): # 从类型注解中获取子对象的类(确保IDE能识别类型) child_cls = cls.__annotations__.get(to_attr) if not child_cls: raise ValueError(f"请为父类的 {to_attr} 属性添加类型注解,以便自动生成转发方法") # 遍历子类的公共方法(排除下划线开头的私有方法) for method_name, method in child_cls.__dict__.items(): if callable(method) and not method_name.startswith('_'): # 定义转发函数 def forward_func(self, *args, **kwargs): return getattr(getattr(self, to_attr), method_name)(*args, **kwargs) # 复制原方法的元信息,保证IDE提示正常 forward_func.__name__ = method_name forward_func.__doc__ = method.__doc__ forward_func.__annotations__ = method.__annotations__ # 将转发函数绑定到父类 setattr(cls, method_name, forward_func) return cls return decorator class Child: def __init__(self): self.b = 1 def dosomething(self, a=1) -> str: """执行子对象的操作,接收可选参数a,返回字符串""" print("child dosomething", a, self.b) return 'returning stuff' def calculate_sum(self, x: int, y: int) -> int: """计算两个整数的和""" return x + y @forward_methods(to_attr='child') class Parent: # 添加类型注解,让装饰器能获取Child类的方法信息 child: Child def __init__(self): self.child = Child() # 测试调用 if __name__ == "__main__": p = Parent() # IDE会自动提示dosomething的参数和返回值 result1 = p.dosomething(5) print(result1) # IDE会提示calculate_sum的参数类型和返回值 result2 = p.calculate_sum(3, 7) print(result2)
方案说明
装饰器核心逻辑:
- 通过父类的类型注解获取子对象的类,确保能提前遍历所有方法
- 为每个子对象的公共方法生成转发函数,函数内部调用子对象的对应方法
- 复制原方法的名称、文档字符串和参数注解,让IDE能正确识别方法信息
为什么优于其他方案:
- 避免手动编写大量重复的转发代码,减少拼写错误和维护成本
- 相比
__getattr__,动态添加的方法会直接存在于父类的属性中,IDE能提前识别并给出代码提示 - 不依赖继承,完全符合需求中的“不使用子类继承”要求
方法2:元类实现(进阶)
如果需要在多个类中复用该逻辑,也可以用元类来实现,原理和装饰器类似,只是在类创建阶段自动处理:
class ForwardMeta(type): def __new__(cls, name, bases, attrs): # 获取子对象的属性名和类(这里假设约定用`_forward_to`指定) forward_to = attrs.get('_forward_to') if forward_to: child_attr, child_cls = forward_to for method_name, method in child_cls.__dict__.items(): if callable(method) and not method_name.startswith('_'): def forward_func(self, *args, **kwargs): return getattr(getattr(self, child_attr), method_name)(*args, **kwargs) forward_func.__name__ = method_name forward_func.__doc__ = method.__doc__ forward_func.__annotations__ = method.__annotations__ attrs[method_name] = forward_func return super().__new__(cls, name, bases, attrs) class Parent(metaclass=ForwardMeta): # 指定要转发的子对象属性名和类 _forward_to = ('child', Child) def __init__(self): self.child = Child()
这种方式适合需要批量处理多个类的场景,同样能保留IDE的代码提示。
内容的提问来源于stack exchange,提问作者Famok
相关产品推荐
相关产品推荐

