首页 > 解决方案 > 将多个类方法应用于对象列表的 Pythonic 方式

问题描述

我有一个带有一些内置方法的类。这是该类可能看起来的抽象示例:

class Foo:
    def __init__(self):
        self.a = 0
        self.b = 0

    def addOneToA(self):
        self.a += 1

    def addOneToB(self):
        self.b += 1

为了简单起见,我将内置方法减少到 2 个,但实际上我的班级接近 20 个。

接下来我有另一个类,旨在处理Foo实例列表。

class Bar:
    def __init__(self, fooInstances):
        self.fooInstances = fooInstances

# Bar([Foo(), Foo(), Foo()])

如果我想将其中一种Foo方法应用于 中的Foo实例Bar怎么办?

class Bar:
    # ...
    def addOneToA(self):
        for fooInstance in self.fooInstances:
            fooInstance.addOneToA()
    
    def addOneToB(self):
        for fooInstance in self.fooInstances:
            fooInstance.addOneToB()

上面的示例是执行我所描述的一种方式,但如果有 20 个Foo. 或者,我可以这样做:

class Bar:
    # ...
    def applyFooMethod(self, func, *args):
        for fooInstance in self.fooInstances:
            fooInstance.func(args)

但我更希望有一些东西可以让我调用并将.addOneToA()Bar应用于. 有没有一种干净的方法可以在不定义inside的所有方法的情况下做到这一点?FooBarFooBar

标签: pythonclass

解决方案


一种方法是__getattr__覆盖Bar

class Bar:
    def __init__(self, fooInstances):
        self.fooInstances = fooInstances

    def __getattr__(self, attr):
        try:
            getattr(self.fooInstances[0], attr)
        except AttributeError:
            raise AttributeError(f"'Bar' object has no attribute '{attr}'")
        else:
            def foo_wrapper(*args, **kwargs):
                for foo_inst in self.fooInstances:
                    getattr(foo_inst, attr)(*args, **kwargs)
            return foo_wrapper 

__getattr__Bar如果对Bar对象的属性查找失败,则调用 on。然后我们尝试查看一个Foo实例是否具有该属性;如果不是,则引发一个,AttributeError因为既不Bar也不Foo接受该属性。但是如果Foo确实有它,我们会返回一个函数,当被调用时,它会在驻留在对象attr中的每个瞬间调用方法 () 。FooBar

用法:

     ...
     # changed this method in Foo to see the passing-an-argument case
     def addOneToA(self, val):
         self.a += 1
         print(f"val = {val}")
     ...


>>> bar = Bar([Foo(), Foo(), Foo()])

>>> bar.addOneToB()
>>> [foo.b for foo in bar.fooInstances]
[1, 1, 1]

>>> bar.addOneToA(val=87)  # could also pass this positionally
val = 87
val = 87
val = 87

>>> bar.this_and_that
AttributeError: 'Bar' object has no attribute 'this_and_that'

推荐阅读