运行时重写第三方类实例__call__方法且保留其他原有方法的方案咨询
方案1:动态Mixin组合(推荐,兼容类型检查)
思路:把不同的__call__实现抽为独立的Mixin类,运行时动态将Mixin和目标第三方类组合为临时子类,无需预先编写所有m×n个子类。
代码实现
# 抽离所有自定义__call__逻辑,每个逻辑仅需编写一次 class CallLogicSub: def __call__(self, x, y): return x - y class CallLogicDiv: def __call__(self, x, y): return x // y
工厂函数用于动态生成实例:
def create_instance(base_cls, call_mixin): # 动态生成子类,Mixin在前保证__call__优先覆盖原类实现 temp_cls = type(f"{base_cls.__name__}With{call_mixin.__name__}", (call_mixin, base_cls), {}) return temp_cls()
使用示例
# 仅需m+n次判断:先选原类、再选call逻辑 # 第一步选原类(m种判断) base_cls = Bar # 实际使用时根据场景判断选Foo/Bar/Baz # 第二步选call逻辑(n种判断) call_mixin = CallLogicSub # 实际使用时根据场景选对应的Mixin instance = create_instance(base_cls, call_mixin) print(instance(2, 3)) # 输出-1,使用自定义CallLogicSub的实现 instance.method2(1,2) # 正常调用Bar原生的method2
优点
- 自定义
__call__逻辑无重复代码 - 生成的实例是原类的子类,
isinstance检查、类属性访问等原生特性完全兼容 - 生成的临时类可复用,适合批量创建同配置实例的场景
方案2:实例包装器(最灵活,无需修改类结构)
思路:编写通用包装类,将自定义__call__和原实例组合,所有非__call__的属性/方法直接转发给原实例,完全不触碰原类实现。
代码实现
class CallOverrideWrapper: def __init__(self, original_inst, custom_call): self._original = original_inst self._custom_call = custom_call def __call__(self, *args, **kwargs): return self._custom_call(*args, **kwargs) # 所有其他属性访问直接转发到原实例 def __getattr__(self, name): return getattr(self._original, name)
如果需要兼容isinstance检查,可让包装类继承自基类A:
class CallOverrideWrapper(A): # 其余实现和上面一致
使用示例
# 先创建原生实例 bar = Bar() # 需要修改__call__时直接包装,完全不影响原类和其他实例 wrapped_bar = CallOverrideWrapper(bar, lambda x,y: x - y) print(wrapped_bar(2,3)) # 输出-1,使用自定义逻辑 wrapped_bar.method2(1,2) # 正常调用Bar原生的method2
优点
- 无需预先定义Mixin,自定义
__call__可以直接传入匿名函数,灵活度最高 - 完全不修改原类,仅作用于单个被包装的实例,符合实例隔离要求
- 适合零散、临时修改实例
__call__的场景
注意事项
- 动态Mixin方案需注意继承顺序,Mixin必须放在基类前面,才能保证
__call__优先使用自定义实现 - 包装器方案如果用到其他魔法方法(如
__str__、__len__),需要在包装类里手动添加转发逻辑,因为魔法方法不会走__getattr__
内容的提问来源于stack exchange,提问作者user3389669
相关产品推荐
相关产品推荐

