代码之家  ›  专栏  ›  技术社区  ›  TinyTheBrontosaurus

包装同质python对象

  •  1
  • TinyTheBrontosaurus  · 技术社区  · 6 年前

    我正在寻找一种方法来拥有一个同质对象的集合,将它们包装到另一个对象中,但是让包装器对象具有与原始对象相同的API,并将相应的API调用转发给其对象成员。

    class OriginalApi:
      def __init__(self):
        self.a = 1
        self.b = "bee"
    
      def do_something(self, new_a, new_b, put_them_together=None):
        self.a = new_a or self.a
        self.b = new_b or self.b
    
        if put_them_together is not None:
          self.b = "{}{}".format(self.a, self.b)
    
      # etc.
    
    class WrappedApi:
      def __init__(self):
        self.example_1 = OriginalApi()
        self.example_2 = OriginalApi()
    

    • 重写整个API

      代码示例:

      class WrappedApi:
        def __init__(self):
          self.example_1 = OriginalApi()
          self.example_2 = OriginalApi()
      
        def do_something(self, new_a, new_b, put_them_together=None):
          self.example_1.do_something(new_a, new_b, put_them_together)
          self.example_2.do_something(new_a, new_b, put_them_together)
      
    • 使用列表和for循环 这将更改对象上的API。也就是说,如果我找不到更优雅的东西,这就是备份解决方案。在这种情况下 WrappedApi 类将不存在。

      代码示例:

      wrapped_apis = [OriginalApi(), OriginalApi()]
      for wrapped_api in wrapped_apis:
        wrapped_api.do_something(1, 2, True)
      
    • 我试过用 Python Object Wrapper ,但我看不出如何让它调用具有相同参数的多个子对象。

    对于任何对用例感兴趣的人来说,它实际上是几个matplotlib的集合 axes 物体。我不想重新实现整个系统 API(它很大),我不想更改所有在axes上进行调用的代码(比如 plot , step 等)

    0 回复  |  直到 6 年前
        1
  •  7
  •   6502    6 年前

    如果您只实现方法,那么 __getattr__ 我能做到

    class Wrapper: 
        def __init__(self, x): 
            self.x = x 
        def __getattr__(self, name): 
            def f(*args, **kwargs): 
                for y in self.x: 
                    getattr(y, name)(*args, **kwargs) 
            return f
    

    例如 x = Wrapper([[], [], []]) 打电话后 x.append(12) 所有三个列表对象的最后一个元素都是12。

    None ... 一个选项可以是收集返回值并将其作为列表返回,但这当然会“破坏API”。

        2
  •  1
  •   darksky    6 年前

    我想你的想法是对的

    wrapped_apis = [OriginalApi(), OriginalApi()]
    for wrapped_api in wrapped_apis:
        wrapped_api.do_something(1, 2, True)
    

    继承 list 然后在创建它的项之后处理对它的API调用。

    class WrapperClass(list):
        def __init__(self, api_type):
            self.api_type = api_type
    
            for func in dir(api_type):
                if callable(getattr(api_type, func)) and not func.startswith("__"):
                    setattr(self, func, lambda *args, **kwargs: 
                        [getattr(o, func)(*args, **kwargs) for o in self])
    
    w = WrapperClass(OriginalApi)
    o1, o2 = [OriginalApi()]*2
    w.append(o1)
    w.append(o2)
    print(w.do_something(1, 2, True))
    # [None, None]
    print(w[0].b)
    # 12
    print(w[1].b)
    # 12
    print(o1.b)
    # 12
    

    在这里,我迭代API类中的每个方法,并在包装类中创建一个方法,将其参数应用于所有列表项。然后返回一个由结果组成的列表。

    不用说,您可能应该验证要附加到该对象的新对象的类型 WrapperClass 就像这样,

    def append(self, item):
        if not isinstance(item, self.api_type):
            raise TypeError('Wrong API type. Expected %s'.format(self.api_type))
        super(WrapperClass, self).append(item)