我正在寻找一种方法来收集同类对象,然后将它们包装在另一个对象中,但使包装对象具有与原始对象相同的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 ,为什么不呢?由于API相当大且正在扩展,因此还不够。必须在多个位置维护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
对象的集合。我不想重新实现整个axes
API(它很大),也不想更改所有在轴上进行调用的代码(例如plot
,step
,等)
答案 0 :(得分:7)
如果您仅实现方法,则通用__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”。
答案 1 :(得分:1)
我认为您在这里有正确的想法
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)