在Python中,有没有办法在迭代的每个项目上调用方法?

时间:2012-12-08 00:32:44

标签: python methods iteration premature-optimization

  

可能重复:
  Is there a map without result in python?

当我想快速/有效地在迭代所包含的每个项目上调用就地方法时,我经常遇到程序中的情况。 (很快意味着for循环的开销是不可接受的)。当我想在每个draw()对象上调用Sprite时,一个很好的例子就是精灵列表。

我知道我可以这样做:

[sprite.draw() for sprite in sprite_list]

但我觉得列表理解被滥用,因为我没有使用返回的列表。 map函数也是如此。请求我过早优化,但我也不希望返回值的开销。

我想知道的是,如果Python中有一种方法可以让我做我刚刚解释过的内容,也许就像我在下面建议的假设函数一样:

do_all(sprite_list, draw)

2 个答案:

答案 0 :(得分:9)

您始终可以编写自己的do_all功能:

def do_all(iterable, func):
    for i in iter(iterable):
       func(i)

然后随时打电话。

使用显式for循环确实没有性能问题。

使用列表推导或map 是一个性能问题,但仅限于构建结果列表。显然,如果你必须在整个过程中建立一个500M的列表,那么迭代超过500M的项目将会慢得多。

值得指出的是,这几乎肯定不会出现像绘制精灵列表之类的东西。你没有500M精灵来绘制。如果你这样做,它可能比创建一个500M的无副本列表要花很多时间。在大多数合理的情况下,你需要对500M对象做同样非常简单的事情,有更好的解决方案,比如切换到numpy。但是有一些可以想象的情况会出现这种情况。

简单的方法是使用生成器表达式或itertools.imap(或者,在Python 3中,只是map),然后通过编写dispose函数来处置值。一种可能性:

def dispose(iterator):
    for i in iterator:
        pass

然后:

dispose(itertools.imap(Sprite.draw, sprite_list))

您甚至可以将do_all定义为:

def do_all(iterable, func):
    dispose(itertools.imap(func, iterable))

如果您为了清晰或简单而这样做,我认为这是错误的。 for循环版本非常容易阅读,而且这个版本看起来像是在尝试用错误的函数名和语法编写Haskell。

如果你是为了表现那么......好吧,如果有真实的表演情况这很重要(这似乎不太可能),你可能想要发挥一堆不同的潜力dispose的实现,并可能将dispose移回do_all以避免额外的函数调用,甚至可能在C中实现整个事务(从中借用快速迭代代码) stdlib的itertools.c)。

或者,更好,pip install more-itertools,然后使用more_itertools.consume。对于它的价值,当前版本只是collections.deque(iterator, maxlen=0),并且在针对自定义C实现的测试中,它的速度慢了不到1%(除非非常小的迭代器 - 我的系统上的截止值为19),所以它是可能不值得在C中实现。但是如果有人这样做,或者如果某个未来的Python(或PyPy)提供了更快的实现方式,那么在你发现它并改变它之前它很可能被添加到more-itertools中。你的代码。

答案 1 :(得分:7)

假设sprite_list是Sprite对象的列表,您可以这样做:

map(Sprite.draw, sprite_list)

这将在sprite_list中的每个项目上调用Sprite.draw(),这与您发布的列表推导基本相同。如果您不想创建列表,可以使用for循环:

for sprite in sprite_list:
    sprite.draw()