func为实例列表的所有成员调用任意类函数

时间:2018-07-13 01:26:51

标签: python python-3.x function class design-patterns

我正在尝试为类_class1_(可能是另一个类)创建一个包装,该包装将包含_class1_的多个实例,一些其他变量以及一些将与之一起使用的函数的列表。该列表并包装了_class1_的所有功能(实际上),实际上应该只使用相同的一组参数为列表中存储的所有_class1_对象调用相应的功能。

有人能建议一个更好的方法,然后手动实现所有这些功能(将遍历列表并从List_class1调用相应的功能)吗?

示例

class class2:
    def __init__(self, N):
         self._N = N
         self.List_class1 = []
         for i in range(0, self._N): 
             self.List_class1.append(class1())

    def function1(self, params)
        for i in range(0, self._N):
               List_class1[i].function1(params)

    def function2(self, params)
        ret = []
        for i in range(0, self._N):
               ret.append(List_class1[i].function2(params))
        return ret

因此,如果在class1中有一个我想适应的函数,也许有人可以提出一种方法来重写它?

2 个答案:

答案 0 :(得分:0)

对您有用的一种方法是为包装器类覆盖__getattr__方法。当找不到实例属性时(即,在主类中未在包装器中实现的任何函数),将调用此方法。

请参阅this question,以了解类似内容,但仅针对单个类而不是其列表。

class Thing:
    def __init__(self, name):
        self.name = name

    def func_1(self):
        print(f"func_1: {self.name}")

    def func_2(self):
        print(f"func_2: {self.name}")


class AttributeGroup:
    def __init__(self):
        self.attributes = []

    def add_attribute(self, function):
        self.attributes.append(function)

    def __call__(self, *args, **kwargs):
        for attr in self.attributes:
            attr(*args, **kwargs)


class AllThings:
    things = []

    def __getattr__(self, key):
        attrs = AttributeGroup()
        for thing in self.things:
            attrs.add_attribute(thing.__getattribute__(key))
        return attrs


group = AllThings()
group.things.append(Thing('a'))
group.things.append(Thing('b'))

group.func_1()
group.func_2()

__getattr__类中的AllThings方法应返回单个属性,因此FunctionGroup类仅将调用传递给它已收集的所有函数。

上面的代码应打印:

func_1: a
func_1: b
func_2: a
func_2: b

然后,您还可以通过某种方式使属性组类可迭代,以便可以将所有属性都包含在列表中,而不仅仅是能够调用所有功能

# AttributeGroup class
def __getitem__(self, item):
    return self.attributes[item]

# main
for each in group.name:
    print(each)

打印:

a
b

答案 1 :(得分:0)

首先,关于此代码的很多事情都是不正确的,或者至少是怪异的:

  • 您有一个共享类属性__N,其中包含一个空列表;但是,在__init__中,您会立即使用一个具有相同名称的实例变量(大概包含一个数字)将其隐藏。因此,该class属性永远不会做任何事情。
  • 也没有理由将其称为__N。出现双下划线的唯一原因是,当您需要防止超类或子类意外使用不兼容的同名名称时。
  • 您正在使用另一个共享的类属性List_class1,但正在__init__中对其进行初始化。这对于例如在该类中注册一个类的所有实例的情况是有意义的,但是对于试图为每个实例创建列表的情况则没有意义。当构造十二个class2(10)时,您希望每个实例具有10个class1实例,不是所有实例都共享相同的120个,对吗?
  • 您的function1function2不是方法,也不能被调用,因为它们不占用self。或者,相反,他们要做采取self,但使用不寻常的名称params。这意味着他们最终将self作为参数传递给class1中的每个函数。

无论如何,看起来您正在尝试创建一种通用代理,但是该代理对对象列表执行“扇出”代理,而不是仅对单个对象进行代理。 / p>

因此,通用代理的相同基本技术将起作用。


例如,您可以使用__getattr__动态地做到这一点:

class class2:
    def __init__(self, N):
        self.c1s = [class1() for _ in range(N)]

    def __getattr__(self, name):
        if name.startswith('_'):
            raise AttributeError(name)
        methods = []
        for c1 in self.c1s:
            attr = getattr(c1, name)
            if callable(attr):
                methods.append(attr)
        if methods:
            def _map(*args, **kw):
                return [method(*args, **kw) for method in methods]
            return _map
        raise AttributeError(name)

class class1:
    def function1(self, x, y):
        return x+y
    def function2(self):
        return 'function 2!'

c2 = class2(3)
print(c2.function1(2, 3))
print(c2.function2())

这将先打印[5, 5, 5],然后再打印["function 2!", "function 2!", "function 2!"]


或者,由于您只想保留class1个实例,因此可以静态地进行。这样做的好处是class2.function2将是您可以在__dict__中看到的真实方法,因此您的交互式解释器可以自动完成此操作,等等。

class class2:
    def __init__(self, N):
        self.c1s = [class1() for _ in range(N)]

for name, value in inspect.getmembers(class1, callable):
    if name.startswith('_'):
        continue
    setattr(class2, name, lambda self, *args, _v=value, **kw: [_v(c1, *args, **kw) for c1 in self.c1s])

好的,最后一个可能会更漂亮,而不是可怕的单线,但希望您能明白。