【问题标题】:funcCalling arbitrary class functions for all members of a list of instancesfunc为实例列表的所有成员调用任意类函数
【发布时间】:2018-07-13 01:26:50
【问题描述】:

我正在尝试为类_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 中有许多我想像这样调整的函数,也许有人可以建议一种方法来做到这一点,而只需重写?

【问题讨论】:

  • 这些函数将如何定义?您在寻找装饰器实现吗?
  • 根据@abarnert 的建议更正了代码

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


【解决方案1】:

首先,这段代码有很多不正确的地方,或者至少很奇怪:

  • 您有一个共享类属性__N 持有一个空列表,但在__init__ 中,您立即使用同名的实例变量将其隐藏起来,大概是一个数字。所以,这个类属性永远不会做任何事情。
  • 也没有理由将其称为__N。使用双下划线的唯一原因是当您需要防止超类或子类意外使用相同名称来表示不兼容的内容。
  • 您正在使用另一个共享类属性List_class1,但您在__init__ 中对其进行了初始化。这对于例如向类注册类的所有实例这样的情况是有意义的,但对于您尝试为每个实例创建列表的情况没有意义。当您构建一打 class2(10)s 时,您希望每个都有 10 个 class1 实例,而不是所有实例都共享相同的 120 个,对吗?
  • 你的function1function2 不是方法,不能被调用,因为它们没有self。或者,更确切地说,他们确实使用self,但使用了不寻常的名称params。这意味着他们最终会将self 作为参数传递给class1 中的每个函数。

无论如何,看起来您要做的是创建一种通用代理,但它对对象列表执行“扇出”代理,而不仅仅是代理单个对象。

因此,通用代理的基本技术同样适用。


例如,您可以使用__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])

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

【讨论】:

    【解决方案2】:

    对您有用的一种方法是覆盖包装类的__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()
    

    AllThings 类中的 __getattr__ 方法应该返回一个属性,因此 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-16
      • 1970-01-01
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多