什么时候应该使用 Map 而不是 For 循环?

这与以下内容有关: (在 Python 代码中)

for i in object:
doSomething(i)

VS

map(doSomething, object)

两者都很容易理解,而且都很短,但是速度上有什么区别吗?现在,如果 doSomething 有一个我们需要检查的返回值,它将作为 map 中的 list 返回,在 for 循环中,我们可以创建自己的 list,或者一次检查一个。

for i in object:
returnValue = doSomething(i)
doSomethingWithReturnValue(returnValue)

VS

returnValue = map(doSomething, object)
map(doSomethingWithReturnValue, returnValue)

现在,我觉得这两者有点分歧。如果在循环过程中动态检查它们,或者在最后一次检查所有它们会产生不同的结果,那么这两个 doSomething WithReturn nValue 函数可能会有所不同。此外,for 循环似乎总是能够工作,也许速度会更慢,因为映射只能在某些场景下工作。当然,我们可以通过扭曲来实现这两种方法中的任何一种,但关键是要避免这种类型的工作。

我所寻找的是一个场景,映射功能在性能、可读性、可维护性或实现速度方面的表现真正优于完美的循环。如果答案是真的没有很大的区别,那么我想知道在实践中人们什么时候使用其中一种或另一种,或者它是否真的完全任意,并根据您的机构的编码标准设置。

谢谢!

70280 次浏览

当您希望将函数应用于迭代器的每个项并返回结果列表时,map非常有用。这比使用 for 循环和构造列表更简单、更简洁。

对于其他情况,for通常更具可读性,并且在 lisp 中有许多迭代构造,它们基本上是使用宏和 map 编写的。因此,在 map不适合的情况下,使用 for循环。

理论上,如果我们有一个足够聪明的编译器/解释器来使用多个 CPU/处理器,那么 map可以实现得更快,因为对每个项目的不同操作可以并行执行。然而,我认为目前情况并非如此。

只要使用列表理解就行了: 它们更像 Python。它们还有类似于生成器表达式的语法,这使得从一个表达式切换到另一个表达式很容易。在将代码转换为 py3k 时,不需要进行任何更改: map返回一个 py3k 中的迭代,并且必须调整代码。

如果你不关心返回值只是不要命名新的列表,你需要使用返回值一次在你的代码你可能切换到生成器表达式和一个单一的列表内涵在最后。

编辑 : 我没有意识到在 python 3.0之后 map等于 itertools.imap。所以这里的结论可能不正确。明天我将在 python2.6上重新运行测试并发布结果。

如果 doSomething非常“小”,那么 map可以比 for循环或列表理解快得多:

# Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32


from timeit import timeit


do = lambda i: i+1


def _for():
for i in range(1000):
do(i)


def _map():
map(do, range(1000))


def _list():
[do(i) for i in range(1000)]


timeit(_for, number=10000)  # 2.5515936921388516
timeit(_map, number=10000)  # 0.010167432629884843
timeit(_list, number=10000) # 3.090125159839033

这是因为 map是用 C 编写的,而 for循环和列表理解在 python 虚拟机中运行。

你熟悉时间模块吗?下面是一些时机。- s 执行一次性设置,然后循环命令并记录最佳时间。

1> python -m timeit -s "L=[]; M=range(1000)" "for m in M: L.append(m*2)"
1000 loops, best of 3: 432 usec per loop


2> python -m timeit -s "M=range(1000);f=lambda x: x*2" "L=map(f,M)"
1000 loops, best of 3: 449 usec per loop


3> python -m timeit -s "M=range(1000);f=lambda x:x*2" "L=[f(m) for m in M]"
1000 loops, best of 3: 483 usec per loop


4> python -m timeit -s "L=[]; A=L.append; M=range(1000)" "for m in M: A(m*2)"
1000 loops, best of 3: 287 usec per loop


5> python -m timeit -s "M=range(1000)" "L=[m*2 for m in M]"
1000 loops, best of 3: 174 usec per loop

注意,除了最后两个,它们都是相似的。正是函数调用(L.append 或 f (x))严重影响了计时。在 # 4中,L.append 查找已经在安装中完成了一次。在 # 5中,没有函数调用的列表比较被使用。

下面是一个循环和映射之间的实验,来证明哪一个更有效:

from time import time


fruit  = range(4_000_000)


tic = time()
res=[True if i % 2 == 0 else False for i in fruit]
toc = time()
print(toc - tic)




tic = time()
list(map(lambda s: s % 2 == 0, fruit))
toc = time()
print(toc - tic)


tic = time()
res = []
for i in fruit:
if i % 2 == 0:
res.append(True)
else:
res.append(False)
toc = time()
print(toc - tic)

退出:

0.29710841178894043
0.4369466304779053
0.5959873199462891

所以第一种方法是最有效的方法。