【发布时间】:2009-05-17 19:45:43
【问题描述】:
map() 是否会像“for”那样遍历列表?使用 map 和 for 有什么价值吗?
如果是这样,现在我的代码如下所示:
for item in items:
item.my_func()
如果有意义,我想将其设为 map()。那可能吗?一个例子是什么样的?
【问题讨论】:
标签: python for-loop map-function
map() 是否会像“for”那样遍历列表?使用 map 和 for 有什么价值吗?
如果是这样,现在我的代码如下所示:
for item in items:
item.my_func()
如果有意义,我想将其设为 map()。那可能吗?一个例子是什么样的?
【问题讨论】:
标签: python for-loop map-function
您可以使用map 代替您显示的for 循环,但由于您似乎没有使用item.my_func() 的结果,因此不推荐。如果您想将没有副作用的函数应用于列表的所有元素,则应使用map。在所有其他情况下,请使用显式 for 循环。
此外,从 Python 3.0 开始,map 返回一个生成器,因此在这种情况下,map 的行为将不同(除非您明确评估生成器返回的所有元素,例如通过在其上调用 list)。
编辑:kibibu 在 cmets 中要求澄清为什么 map 的第一个参数不应是具有副作用的函数。我会尝试回答这个问题:
map 意味着传递一个函数 f in the mathematical sense。在这种情况下,将f 应用于第二个参数的元素的顺序无关紧要(当然,只要它们以原始顺序返回)。更重要的是,在这种情况下,map(g, map(f, l)) 在语义上等同于map(lambda x: g(f(x)), l),无论f 和g 应用于各自输入的顺序如何。
例如,map 是否同时返回和迭代器或完整列表都没有关系。然而,如果f 和/或g 引起副作用,那么只有当map(g, map(f, l)) 的语义在任何阶段g 应用于第一个n 在map(f, l) 之前由map(f, l) 返回的元素将f 应用于l 的(n + 1)st 元素。 (这意味着map 必须执行最惰性的迭代——它在 Python 3 中会这样做,但在 Python 2 中不会!)
更进一步:即使我们假设 map 的 Python 3 实现,如果 map(f, l) 的输出是例如在提供给外部map 调用之前通过itertools.tee。
上面的讨论似乎是理论上的,但随着程序变得越来越复杂,它们变得更难推理,因此也更难调试。确保某些东西是不变的可以在一定程度上缓解这个问题,实际上可以防止一整类错误。
最后,map 让许多人想起它在各种(纯)函数式语言中的真正函数式对应物。传递给它一个带有副作用的“函数”会让那些人感到困惑。因此,作为替代方案(即使用显式循环)并不比调用map 更难实现,强烈建议将map 的使用限制在要应用的函数的情况下不会引起副作用。
【讨论】:
map 时可以将 作为附加参数引用,但我不认为 Python(当前)对那种。)
你可以像这样使用 map 来写这个:
map(cls.my_func, items)
将 cls 替换为您正在迭代的项目的类。
如 Stephan202 所述,在这种情况下不推荐。
通常,如果您想通过对列表中的每个项目应用某些函数来创建一个新列表,请使用 map。这意味着该函数没有副作用,因此您可以(可能)并行运行地图。
如果您不想创建新列表,或者该函数有副作用,请使用 for 循环。您的示例就是这种情况。
【讨论】:
存在细微的语义差异,这可能在 python 语言规范中是封闭的。 map 是显式可并行化的,而 for 仅在特殊情况下。代码可以break从for中跳出,但只能从map中跳出异常。
在我看来,map 也不应该保证函数应用的顺序,而 for 必须。 AFAIK 目前没有 python 实现能够执行此自动并行化。
【讨论】:
如果需要,您可以将您的 map 切换到一些很酷的线程或多处理或分布式计算框架。 Disco 是基于 erlang-and-python 的分布式、抗故障框架的示例。我在 2 个 8 核的盒子上配置了它,现在我的程序运行速度快了 16 倍,这要归功于 Disco 集群,但是我不得不从列表推导和 for 循环重写我的程序以映射/减少。
使用 for 循环和列表推导以及 map/reduce 编写程序是一样的,但是当您需要它在集群上运行时,如果您使用 map/reduce,您几乎可以免费完成。如果你没有,那么,你将不得不重写。
注意:据我所知,python 2.x 从 map 返回一个列表而不是迭代器。我听说可以使用iter.imap() 绕过它(虽然从未使用过)。
【讨论】:
当您不需要返回结果列表(例如具有副作用的函数)时,请使用显式 for 循环。
当您确实需要返回结果列表时使用列表推导式(例如,直接根据输入返回值的函数)。
当你试图让 Lisp 用户相信 Python 值得使用时,请使用 map()。 ;)
【讨论】:
map 的主要优点是当您想要获取列表中每个元素的某些计算结果时。例如,这个 sn-p 将列表中的每个值加倍:
map(lambda x: x * 2, [1,2,3,4]) #=> [2, 4, 6, 8]
请务必注意,map 返回一个包含结果的新列表。它不会修改原始列表。
要对for 执行相同的操作,您必须创建一个空列表并在for 正文中添加额外的行,以将每个计算的结果添加到新列表中。 map 版本更加简洁实用。
【讨论】:
对于内置函数,Map 有时比手动编写 for 循环更快。尝试时序图(str, range(1000000)) 与类似的 for 循环。
【讨论】:
map(lambda item: item.my_func(), items)
【讨论】: