【问题标题】:Is there a value in using map() vs for?使用 map() 和 for 有什么价值吗?
【发布时间】:2009-05-17 19:45:43
【问题描述】:

map() 是否会像“for”那样遍历列表?使用 map 和 for 有什么价值吗?

如果是这样,现在我的代码如下所示:

for item in items:
    item.my_func()

如果有意义,我想将其设为 map()。那可能吗?一个例子是什么样的?

【问题讨论】:

    标签: python for-loop map-function


    【解决方案1】:

    您可以使用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)无论fg 应用于各自输入的顺序如何

    例如,map 是否同时返回和迭代器或完整列表都没有关系。然而,如果f 和/或g 引起副作用,那么只有当map(g, map(f, l)) 的语义在任何阶段g 应用于第一个nmap(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 的使用限制在要应用的函数的情况下不会引起副作用。

    【讨论】:

    • 为什么不推荐?它是语义的东西,还是某种分布式处理的东西(副作用会破坏并行性)?
    • @kibibu:我开始回答你的问题,但最终得到的文本太多,无法包含在此评论字段中。所以我更新了答案:) 这有什么帮助! (至于您关于可迭代对象的分布式处理的评论:尽管在用其他语言谈论 map 时可以将 作为附加参数引用,但我不认为 Python(当前)对那种。)
    【解决方案2】:

    你可以像这样使用 map 来写这个:

    map(cls.my_func, items)
    

    将 cls 替换为您正在迭代的项目的类。

    如 Stephan202 所述,在这种情况下不推荐

    通常,如果您想通过对列表中的每个项目应用某些函数来创建一个新列表,请使用 map。这意味着该函数没有副作用,因此您可以(可能)并行运行地图。

    如果您不想创建新列表,或者该函数有副作用,请使用 for 循环。您的示例就是这种情况。

    【讨论】:

      【解决方案3】:

      存在细微的语义差异,这可能在 python 语言规范中是封闭的。 map 是显式可并行化的,而 for 仅在特殊情况下。代码可以breakfor中跳出,但只能从map中跳出异常。

      在我看来,map 也不应该保证函数应用的顺序,而 for 必须。 AFAIK 目前没有 python 实现能够执行此自动并行化。

      【讨论】:

        【解决方案4】:

        如果需要,您可以将您的 map 切换到一些很酷的线程或多处理或分布式计算框架。 Disco 是基于 erlang-and-python 的分布式、抗故障框架的示例。我在 2 个 8 核的盒子上配置了它,现在我的程序运行速度快了 16 倍,这要归功于 Disco 集群,但是我不得不从列表推导和 for 循环重写我的程序以映射/减少。

        使用 for 循环和列表推导以及 map/reduce 编写程序是一样的,但是当您需要它在集群上运行时,如果您使用 map/reduce,您几乎可以免费完成。如果你没有,那么,你将不得不重写。

        注意:据我所知,python 2.x 从 map 返回一个列表而不是迭代器。我听说可以使用iter.imap() 绕过它(虽然从未使用过)。

        【讨论】:

          【解决方案5】:

          当您不需要返回结果列表(例如具有副作用的函数)时,请使用显式 for 循环。

          当您确实需要返回结果列表时使用列表推导式(例如,直接根据输入返回值的函数)。

          当你试图让 Lisp 用户相信 Python 值得使用时,请使用 map()。 ;)

          【讨论】:

            【解决方案6】:

            map 的主要优点是当您想要获取列表中每个元素的某些计算结果时。例如,这个 sn-p 将列表中的每个值加倍:

            map(lambda x: x * 2, [1,2,3,4])  #=> [2, 4, 6, 8]
            

            请务必注意,map 返回一个包含结果的新列表。它不会修改原始列表。

            要对for 执行相同的操作,您必须创建一个空列表并在for 正文中添加额外的行,以将每个计算的结果添加到新列表中。 map 版本更加简洁实用。

            【讨论】:

            • 列表推导更加简洁,例如[x*2 for x in [1,2,3,4]) 在您的示例中。
            【解决方案7】:

            对于内置函数,Map 有时比手动编写 for 循环更快。尝试时序图(str, range(1000000)) 与类似的 for 循环。

            【讨论】:

              【解决方案8】:
              map(lambda item: item.my_func(), items)
              

              【讨论】:

              • 这确实有效,但没有回答为什么这比使用 for 循环更好(即它不是)的问题。
              猜你喜欢
              • 2018-03-08
              • 2010-10-03
              • 1970-01-01
              • 2013-10-31
              • 2021-11-14
              • 2011-09-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多