当我想快速/有效地在迭代所包含的每个项目上调用就地方法时,我经常遇到程序中的情况。 (很快意味着for循环的开销是不可接受的)。当我想在每个draw()
对象上调用Sprite
时,一个很好的例子就是精灵列表。
我知道我可以这样做:
[sprite.draw() for sprite in sprite_list]
但我觉得列表理解被滥用,因为我没有使用返回的列表。 map
函数也是如此。请求我过早优化,但我也不希望返回值的开销。
我想知道的是,如果Python中有一种方法可以让我做我刚刚解释过的内容,也许就像我在下面建议的假设函数一样:
do_all(sprite_list, draw)
答案 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()