如何为对象列表调用相同的方法?


78

假设这样的代码:

class Base:
    def start(self):
        pass
    def stop(self)
        pass

class A(Base):
    def start(self):
        ... do something for A
    def stop(self)
        .... do something for A

class B(Base):
    def start(self):

    def stop(self):

a1 = A(); a2 = A()
b1 = B(); b2 = B()

all = [a1, b1, b2, a2,.....]

现在,我要为所有列表中的每个对象调用方法start和stop(也许还有其他)。除了编写像这样的一堆函数外,有什么优雅的方法可以做到这一点

def start_all(all):
    for item in all:
        item.start()

def stop_all(all):

1
一堆功能是两个功能?
SilentGhost 2010年

4
这与您的问题无关,但是在您的示例中没有明显的理由为什么您需要该基类。Python将非常乐意为您提供不相关对象的列表,只要它们都具有“开始”和“停止”方法,您仍然可以通过它们调用这些方法来进行迭代。
邓肯

1
Base使用无用的方法进行定义,然后在Python中定义行为AB反映出不良的设计。而不是使用一个抽象基类,你可以定义AB和他们分享的接口使用它们互换只要。您当前的处理方式会创建一个无用的类,这只是您不需要的额外内容。
Mike Graham 2010年

Answers:


7

* _all()函数非常简单,以至于我只想编写一些方法。如果您有很多相同的功能,则可以编写一个通用功能:

def apply_on_all(seq, method, *args, **kwargs):
    for obj in seq:
         getattr(obj, method)(*args, **kwargs)

或创建一个函数工厂:

def create_all_applier(method, doc=None):
    def on_all(seq, *args, **kwargs):
        for obj in seq:
            getattr(obj, method)(*args, **kwargs)
    on_all.__doc__ = doc
    return on_all

start_all = create_all_applier('start', "Start all instances")
stop_all = create_all_applier('stop', "Stop all instances")
...

56
这并不比直接编写for循环更简单。
Mike Graham 2010年

177

这会工作

all = [a1, b1, b2, a2,.....]

map(lambda x: x.start(),all)    

简单的例子

all = ["MILK","BREAD","EGGS"]
map(lambda x:x.lower(),all)
>>>['milk','bread','eggs']

并在python3中

all = ["MILK","BREAD","EGGS"]
list(map(lambda x:x.lower(),all))
>>>['milk','bread','eggs']

9
在Python 3中,map返回一个地图对象,而不是在Python 2中返回一个列表。我不确定标准过程,但是我认为我应该更正它,因此我提交了一个编辑。因此,代码现在应该在这两个Python 2和Python 3的预期方式工作并且正确
leetNightshade

1
同样,这感觉像是更Python化的答案,应该被接受。map是一个不错的选择,我之前从未使用过lambda,所以谢谢。
leetNightshade

6
在Python 3中,map()函数调用是不够的。它将创建一个generator对象,但不会在每个list元素上应用lambda。Lambda将应用于遍历map()结果。list(map(...))将解决问题。
Alex Musayev

24
@dreikanter还是干什么[x.start() for x in all]
endlith 2014年

6
使用列表然后将其丢弃会给我留下非Pythonic的印象。因为调用的方法是带有副作用的命令性代码,所以显式for循环更惯用且对读者而言不那么令人惊讶。如果确实需要,可以编写def foreach(fun, gen): for i in gen: fun(gen)
托马斯(Thomas)

32

似乎会有一种更Python化的方法来执行此操作,但我尚未找到它。

如果我要在一堆对象上调用相同的函数(而不是方法),有时会使用“ map”:

map(do_something, a_list_of_objects)

这将替换一堆看起来像这样的代码:

 do_something(a)
 do_something(b)
 do_something(c)
 ...

但也可以通过行人“ for”循环来实现:

  for obj in a_list_of_objects:
       do_something(obj)

缺点是:a)您正在创建一个列表,将其作为从“ map”中被丢弃的返回值; b)仅仅使用简单的循环变量可能会更加令人困惑。

您还可以使用列表理解,但这也有点滥用(再次,创建一个一次性列表):

  [ do_something(x) for x in a_list_of_objects ]

对于方法,我想这些方法中的任何一个都可以(保留相同):

map(lambda x: x.method_call(), a_list_of_objects)

要么

[ x.method_call() for x in a_list_of_objects ]

因此,实际上,我认为行人(迄今有效)的“ for”循环可能是您最好的选择。


4
我的第一个反应是map(lambda x:x.member(),list)。感觉像是一个很清晰的班轮
Mark Essel

关于“行人” for循环的良好结论。如果这是CL你必须mapcarmapc的“希望列表/扔掉列表”的情况加以区分。但是a ...
Joao Tavora

21

该方法

for item in all:
    item.start()

简单,容易,可读和简洁。这是Python为此操作提供的主要方法。如果有帮助,您当然可以将其封装在函数中。为此定义一个通用的特殊功能可能比仅写for循环还不清楚。


7

也许map,但是由于您不想列出清单,因此您可以编写自己的清单...

def call_for_all(f, seq):
    for i in seq:
        f(i)

那么您可以执行以下操作:

call_for_all(lamda x: x.start(), all)
call_for_all(lamda x: x.stop(), all)

顺便说一下,所有都是内置函数,请不要覆盖它;-)


6

从Python 2.6开始,提供了operator.methodcaller函数。

因此,您可以获得更优雅(更快速)的东西:

from operator import methodcaller

map(methodcaller('method_name'), list_of_objects)

1
python3的最佳方法是什么?
尼尔

@Neil在python3中有效,唯一不同的是map在3中返回迭代器,而不是列表
notbad.jpeg

3

通过@Ants Aasmas进一步回答问题,您可以创建一个包装器,该包装器接受任何方法调用并将其转发给给定列表的所有元素:

class AllOf:
    def __init__(self, elements):
        self.elements = elements
    def __getattr__(self, attr):
        def on_all(*args, **kwargs):
            for obj in self.elements:
                getattr(obj, attr)(*args, **kwargs)
        return on_all

该类可以这样使用:

class Foo:
    def __init__(self, val="quux!"):
        self.val = val
    def foo(self):
        print "foo: " + self.val

a = [ Foo("foo"), Foo("bar"), Foo()]
AllOf(a).foo()

产生以下输出:

foo:foo
foo:栏
富:!

通过一些工作和独创性,可能还可以增强它来处理属性(返回属性值列表)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.