何时应使用 Map 而非 For 循环?

80 投票
5 回答
80243 浏览
提问于 2025-04-15 17:27

这段内容和下面的内容有关:(在Python代码中)

for i in object:
     doSomething(i)

map(doSomething, object)

这两种写法都很简单易懂,而且都很简短,但它们在速度上有没有区别呢?现在,如果doSomething这个函数有返回值,我们需要检查这个返回值,那么在使用map的时候,它会以列表的形式返回,而在for循环中,我们可以选择自己创建一个列表,或者一个一个地检查。

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

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

现在,我觉得这两者有点不同。两个doSomethingWithReturnValue函数可能会因为我们在循环中是边走边检查,还是最后一次性检查而产生不同的结果。而且,似乎for循环总是能工作,可能会慢一些,而map函数只有在特定情况下才能工作。当然,我们可以做一些复杂的操作让它们都能工作,但这样做的目的就是为了避免这种麻烦。

我想要找的是一个场景,在这个场景中,使用map函数在性能、可读性、可维护性或实现速度上比一个写得很好的for循环更出色。如果答案是其实没有太大区别,那我想知道在实际中人们是如何选择使用其中一个,或者这完全是随意的,取决于你所在机构的编码标准。

谢谢!

5 个回答

11

直接使用列表推导式吧:这样更符合Python的风格。而且它的语法和生成器表达式很相似,这样在两者之间切换就很方便。如果你把代码转换到Python 3的时候,不需要改动什么:在Python 3中,map会返回一个可迭代对象,你需要调整你的代码。

如果你不在乎返回值,那就不用给新列表命名。如果你在代码中需要用到返回值,可能会考虑使用生成器表达式,最后再用一个列表推导式。

27

你知道timeit模块吗?下面是一些时间测试的结果。-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个测试中,使用的是没有函数调用的列表推导。

53

map 是一个很有用的工具,当你想对一个可迭代的对象里的每个项目都应用一个函数,并返回结果的列表时,它就派上用场了。用它比起用循环来构建列表要简单明了得多。

for 循环在其他情况下通常更容易让人理解。在 Lisp 语言中,有很多迭代的方式基本上是通过宏和 map 来实现的。所以在 map 不太适合的情况下,就用 for 循环吧。

理论上,如果我们有一个足够聪明的编译器或解释器,能够利用多个 CPU 或处理器,那么 map 的执行速度可以更快,因为对每个项目的不同操作可以同时进行。不过,我觉得现在还没有这样的情况。

撰写回答