假设您有一个返回 None
的函数(有趣的部分是副作用,例如日志记录、更新、保存、通过管道推送...),或者您不返回的值确实需要。现在你想把这个函数应用到一个可迭代的对象上。你是怎么做到的?
在某些情况下使用 map
既不优雅也不高效,因为我们需要使用它来执行它。
_ = list(map(print, ['hello', 'world']))
如果可迭代对象很小,那很好,但你不想对大的可迭代对象这样做。所以你可以这样做:
def apply(func, *iterables):
for items in zip(*iterables):
func(*items)
这会给我们想要的效果:
>>> apply(print, ['hello', 'world'])
hello
world
>>> apply(lambda x, y: print(x + y), [1, 2], [3, 4])
4
6
>>> from collections import Counter
>>> c = Counter()
>>> apply(c.update, ['green', 'eggs'])
>>> c
Counter({'g': 3, 'r': 1, 'e': 3, 'n': 1, 's': 1})
难道没有内置技巧吗?因为 map
对于较小的可迭代对象来说似乎更快,但是内存成本很高,我想这在某些时候开始使它成为最慢的选项:
>>> from timeit import timeit
>>> from collections import Counter
>>>
>>> iterable = ['green', 'eggs', 'and', 'ham'] * 10000
>>> timeit("c = Counter(); apply(c.update, iterable);", number=100, globals=globals())
2.7703
>>> timeit("c = Counter(); _ = list(map(c.update, iterable));", number=100, globals=globals())
2.6981
>>>
>>> iterable = ['green', 'eggs', 'and', 'ham'] * 100000
>>> timeit("c = Counter(); apply(c.update, iterable);", number=100, globals=globals())
27.4498
>>> timeit("c = Counter(); _ = list(map(c.update, iterable));", number=100, globals=globals())
30.4676
最佳答案
Python 内置函数中没有任何内容是专门为此目的而制作的。但是,有一些设计注意事项。
存在内存/速度权衡。如果您使用列表理解,则会有一个结果列表,该列表存储在内存中。
# the result is not assigned anywhere, but the list is still created
[my_func(*args) for args in somme_iterable]
是的,None
是一个单例,但每个列表项的开销很小;对于非常大的可迭代对象,它可能很大。如果您使用 for
循环,您可以简单地省略结果。
for args in some_iterable:
my_func(*args) # result is never stored
虽然现在不鼓励 map
的 se 支持列表理解,但它可能会带来一些好处。如果该函数是无状态的,您可以将 map 替换为并行或异步版本;你不能对列表理解或显式循环做同样的事情。然而,对于更小/更简单的情况,这可能有点矫枉过正。
# imagine we have a module providing a thread/process pool
# there is a ton of reference implementations including here on SO
from my_threadlib import map
# ... the rest of code remains unchanged
map(my_func, iterable)
无论如何,这里的判断是主观的,更多地取决于您的代码风格实践和将要使用它的上下文。
关于当我们不关心输出时,Python 内置习惯用法将函数应用于可迭代对象的每个元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62311774/