3

可能重复:
在 python 中是否有没有结果的地图?

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

我知道我可以做这样的事情:

[sprite.draw() for sprite in sprite_list]

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

我想知道的是Python中是否有一种方法可以让我做我刚才解释的事情,也许就像我在下面建议的假设函数:

do_all(sprite_list, draw)

4

2 回答 2

10

您始终可以编写自己的do_all函数:

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

然后随时调用它。

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

使用列表推导或 存在性能问题,map仅在构建结果列表时。显然,如果您必须在此过程中构建一个 500M 的列表,那么迭代超过 500M 的项目会慢很多。

值得在这里指出的是,对于像绘制精灵列表这样的事情,几乎可以肯定不会出现这种情况。您没有 500M 的精灵要绘制。如果你这样做了,它可能会比创建一个包含 5 亿份无副本的列表要长得多。在大多数可能的情况下,您确实需要对 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,并可能将其移disposedo_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在你发现它并更改你的代码之前被添加进去。

于 2012-12-08T00:40:22.257 回答
7

假设 sprite_list 是 Sprite 对象的列表,您可以执行以下操作:

map(Sprite.draw, sprite_list)

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

for sprite in sprite_list:
    sprite.draw()
于 2012-12-08T00:36:59.567 回答