Python动态包装器实现需求:基于对象数据暴露API调用
实现动态包装器类,自动暴露被包装对象的API
你现在的静态Wrapper类确实能处理Concrete对象,但局限性很明显:每新增一种要包装的类,就得手动给Wrapper加对应的转发方法,扩展性太差。要实现动态适配任意对象的包装器,Python的__getattr__魔法方法就是完美的解决方案。
先补全你原来的静态实现示例,方便对比:
class Concrete: def __init__(self, data): self.data = data def print_data(self): print(self.data) class Wrapper: ''' 未来将支持包装多种对象,目前仅能处理Concrete对象。 ''' def wrap_it(self, concrete): self.cco = concrete # concreteobject=cco def print_data(self): self.cco.print_data() # 静态实现的使用 cco = Concrete(5) wc = Wrapper() wc.wrap_it(cco) wc.print_data() # 输出: 5
这个实现的问题在于:如果Concrete新增方法,或者你要包装另一个完全不同的类,必须修改Wrapper类手动添加转发逻辑,效率极低。
动态包装器的实现方案
下面是可以自动适配任意被包装对象的动态Wrapper类,核心就是利用__getattr__自动转发属性和方法调用:
class Concrete: def __init__(self, data): self.data = data def print_data(self): print(self.data) def double_data(self): self.data *= 2 return self.data class DynamicWrapper: ''' 支持动态包装任意对象,自动暴露被包装对象的所有API ''' def __init__(self, wrapped_obj=None): self._wrapped = wrapped_obj # 下划线标记为内部私有属性 def wrap_it(self, wrapped_obj): self._wrapped = wrapped_obj def __getattr__(self, attr_name): # 当访问Wrapper没有的属性/方法时,转发给被包装对象 if not self._wrapped: raise ValueError("还没有包装任何对象!请先调用wrap_it()传入目标对象。") if hasattr(self._wrapped, attr_name): return getattr(self._wrapped, attr_name) # 如果被包装对象也没有这个属性,抛出标准的AttributeError raise AttributeError(f"'DynamicWrapper' 对象没有属性 '{attr_name}'") # 测试动态包装器 if __name__ == "__main__": # 包装Concrete对象 cco = Concrete(5) wrapper = DynamicWrapper() wrapper.wrap_it(cco) # 直接调用被包装对象的方法和属性 wrapper.print_data() # 输出: 5 print(wrapper.double_data()) # 输出: 10 print(wrapper.data) # 输出: 10 # 切换包装另一个完全不同的类 class GreetingService: def say_hello(self, name): return f"Hey, {name}!" def version(self): return "v1.0" greet_obj = GreetingService() wrapper.wrap_it(greet_obj) print(wrapper.say_hello("Ray")) # 输出: Hey, Ray! print(wrapper.version()) # 输出: v1.0
方案核心原理
__getattr__是Python的魔法方法,只有当访问的属性/方法在当前对象中不存在时才会触发。我们在这里把请求转发给被包装的_wrapped对象,让Wrapper变成一个“透明代理”。- 这个实现完全不需要提前知道被包装对象的API结构,不管是Concrete还是其他任意类,只要是合法的Python对象,Wrapper都能自动适配。
- 保留
wrap_it方法,支持后续随时切换被包装的对象,灵活性拉满。
可选优化:初始化直接传入对象
如果你不想每次单独调用wrap_it,可以让__init__直接接受被包装对象,使用起来更便捷:
class DynamicWrapper: def __init__(self, wrapped_obj=None): self._wrapped = wrapped_obj def wrap_it(self, wrapped_obj): self._wrapped = wrapped_obj def __getattr__(self, attr_name): if not self._wrapped: raise ValueError("还没有包装任何对象!请先调用wrap_it()传入目标对象。") if hasattr(self._wrapped, attr_name): return getattr(self._wrapped, attr_name) raise AttributeError(f"'DynamicWrapper' 对象没有属性 '{attr_name}'") # 直接在初始化时传入对象 wrapper = DynamicWrapper(Concrete(7)) wrapper.print_data() # 输出:7
这样你的动态包装器就完全满足需求了,不管后续要包装什么类型的对象,都不需要修改Wrapper的代码,直接使用即可。
内容的提问来源于stack exchange,提问作者Ray Salemi
相关产品推荐
相关产品推荐

