使用map()和for有价值吗?

10 投票
9 回答
1850 浏览
提问于 2025-04-15 11:39

map()函数是不是像"for"循环那样遍历列表?使用map和使用for有什么不同吗?

如果是这样的话,我现在的代码是这样的:

for item in items:
    item.my_func()

如果可以的话,我想把它改成map()。这样做可以吗?能给个例子吗?

9 个回答

2

这里有一点语义上的区别,这可能在Python的语言规范中有说明。map是可以明确并行处理的,而for只有在特定情况下才能做到。代码可以在for循环中提前退出,但在map中只能通过抛出异常来逃离。

在我看来,map不应该保证函数应用的顺序,而for则必须保证顺序。根据我所知,目前没有任何Python的实现能够自动进行并行处理。

5

你可以用 map 这样写:

map(cls.my_func, items)

把 cls 替换成你正在遍历的项目的类名。

正如 Stephan202 提到的,这种做法在这种情况下不推荐

一般来说,如果你想通过对列表中的每个项目应用某个函数来创建一个新列表,就用 map。这样做的意思是这个函数不会有副作用,因此你可以(有可能)并行运行这个 map。

如果你不想创建新列表,或者这个函数有副作用,就用 for 循环。这就是你例子中的情况。

24

你可以用 map 来代替你展示的 for 循环,但因为你似乎没有使用 item.my_func() 的结果,所以不推荐这样做。map 适合用在你想对列表中的每个元素应用一个没有副作用的函数时。在其他情况下,还是用普通的 for 循环吧。

另外,从 Python 3.0 开始,map 返回的是一个迭代器,这样的话 map 的表现就不一样了(除非你明确地把迭代器返回的所有元素都计算出来,比如通过调用 list)。


编辑kibibu 在评论中询问为什么 map 的第一个参数不应该是有副作用的函数。我来试着回答一下这个问题:

map 是用来传递一个数学意义上的函数 f。在这种情况下,f 应用到第二个参数的元素的顺序并不重要(只要它们在返回时保持原来的顺序)。更重要的是,在这种情况下,map(g, map(f, l))map(lambda x: g(f(x)), l) 是语义上等价的,无论 fg 是如何应用到它们各自的输入的。

例如,map 是返回一个迭代器还是一次性返回一个完整的列表并不重要。但是,如果 f 和/或 g 造成了副作用,那么只有在 map(g, map(f, l)) 的语义是这样的:在任何阶段 g 都是应用到 map(f, l) 返回的前 n 个元素上,然后 map(f, l) 才会对第 (n + 1) 个元素应用 f。这意味着 map 必须执行最懒惰的迭代——在 Python 3 中是这样做的,但在 Python 2 中不是!

更进一步:即使我们假设 Python 3 的 map 实现,语义上的等价性也可能会因为 map(f, l) 的输出被例如 itertools.tee 处理后再传给外层的 map 调用而轻易破坏。

以上讨论看起来可能有点理论性,但随着程序变得越来越复杂,它们也变得更难以理解,因此调试起来也更困难。确保某些东西是恒定的,能在一定程度上缓解这个问题,甚至可能防止一类错误的发生。

最后,map 让很多人想起它在各种(纯)函数式语言中的真正对应物。如果把一个有副作用的“函数”传给它,会让这些人感到困惑。因此,考虑到替代方案(即使用显式循环)并不比调用 map 更难实现,强烈建议将 map 的使用限制在那些不会造成副作用的函数上。

撰写回答