Wrapping homogeneous Python objects

If you're only implementing methods then a generic __getattr__ can do the trick

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

For example with x = Wrapper([[], [], []]) after calling x.append(12) all the three list objects will have 12 as last element.

Note that the return value will always be None... an option could be collecting return values and returning them as a list but this of course would "break the API".


I think you have the right idea here

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

You can define your wrapper class by inheriting from list and then handle the API calls to its items once it is created.

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

Here, I'm iterating every method in your API class and creating a method in the wrapper class that applies its arguments to all its list items. It then returns a list comprehension consisting of the results.

Needless to say, you should probably validate the type of a new object being appended to this WrapperClass like so,

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)

Tags:

Python