【问题标题】:Non-lazy evaluation version of map in Python3?Python3中地图的非懒惰评估版本?
【发布时间】:2013-08-25 20:41:27
【问题描述】:

我正在尝试在 Python3 中使用 map。这是我正在使用的一些代码:

import csv

data = [
    [1],
    [2],
    [3]
]

with open("output.csv", "w") as f:
    writer = csv.writer(f)
    map(writer.writerow, data)

但是,由于 Python3 中的 map 返回一个迭代器,因此该代码在 Python3 中不起作用(但在 Python2 中可以正常工作,因为该版本的 map 总是返回 list

我目前的解决方案是在迭代器上添加一个list 函数调用以强制评估。但这似乎很奇怪(我不关心返回值,为什么要将迭代器转换为列表?)

有更好的解决方案吗?

【问题讨论】:

  • 使用map 获得副作用是很奇怪的。 Python 2 map 也收集返回值。新行为只是进一步突出了它。不要那样做,use a for loop
  • @delnan 谢谢你的链接,确实我不应该使用map 来获得副作用。
  • 对于 Python 3,list(map(lambda x:2*x, [1,2,3]))

标签: python


【解决方案1】:

即使在 Python2.x 中,当您对返回的值不感兴趣时​​,使用 map 的副作用(例如函数调用)也是不可取的。如果函数返回 None,但重复了一百万次 - 您将构建一个包含一百万个 Nones 的列表,只是为了丢弃它。正确的方法是使用 for 循环并调用:

for row in data:
    writer.writerow(row)

或者在csv 模块允许的情况下,使用:

writer.writerows(data)

如果出于某种原因您真的非常想使用map,那么您可以使用来自itertools 的consume 配方并生成一个零长度双端队列,例如:

from collections import deque
deque(map(writer.writerow, data), maxlen=0)

【讨论】:

    【解决方案2】:

    您可以设置zero length deque 来执行此操作:

    with open("output.csv", "w") as f:
        writer = csv.writer(f)
        collections.deque(map(writer.writerow, data),0)
    

    这与itertools.consume(iterator, None) 配方的工作方式相同。它在功能上将耗尽迭代器而不构建列表。

    您也可以只使用来自itertoolsconsume recipe

    但循环对我来说更具可读性和 Pythonic,但 YMMV。

    【讨论】:

      【解决方案3】:

      如果您不关心返回值,那么map 不是工作的最佳工具。一个简单的for 会更好:

      for d in data:
          writer.writerow(d)
      

      这在 Python 2.x 和 3.x 中可以正常工作。请注意,map 在您想要创建一个新列表时很有用,如果您只是为了效果而遍历一个可迭代对象,那么请使用 for

      【讨论】:

        【解决方案4】:
        list(map(lambda x: do(x),y))
        

        将触发评估并保持良好、可读的语义,从而提高人类运行时的效率,超越“for-loop(它本身是一个映射)加上新的段落范围转换”语义。 ¯\(ツ)

        请注意,在初稿期间没有理由不将其称为语义糖(事实上,for 循环通常更容易,因为它们更加模块化:在第一次尝试时,您可能不知道代码需要做什么问题),但是当您对处于工作状态的代码进行产品化或逆向工程时,提高语义效率(或者甚至只是重写为相当好的代码)是成功的重要因素。

        无论如何,如果您想刷新map 堆栈,请使用list 类型转换来触发它。


        所以:

        import csv
        
        data = [
            [1],
            [2],
            [3]
        ]
        
        with open("output.csv", "w") as f:
            writer = csv.writer(f)
            list(map(writer.writerow, data))
        

        【讨论】:

          【解决方案5】:

          你也可以使用列表推导,as suggested in the official FAQ:

          with open("output.csv", "w") as f:
              writer = csv.writer(f)
              [writer.writerow(elem) for elem in data]
          

          即使您没有将新创建的列表分配给任何变量,列表理解也会强制评估每个元素。

          但请注意,该列表仍可能在幕后创建,从而造成潜在的性能缺陷,因此虽然相对简洁,但如果输入序列可能会变得很长,则不应使用它。

          【讨论】:

            【解决方案6】:

            我会使用这样的函数从可迭代对象中提取数据:

            def rake(what, where=None):
                for i in what: 
                    if where: where.append(i)
            
            rake(map(writer.writerow, data))
            

            如果您事先知道您永远不会收集映射函数的输出,那么您可以将其简化为:

            for i in what: pass
            

            但是,除非您提供一个列表来放入多余的数据,否则这两种方法都不会保留多余的数据。而且这种方法应该同样适用于 map、filter、reduce、generators、range 以及您可以传递给 rake 函数的任何其他内容那 for 可以迭代。

            【讨论】:

              猜你喜欢
              • 2013-08-28
              • 2017-04-30
              • 1970-01-01
              • 1970-01-01
              • 2019-01-07
              • 2017-01-13
              • 2015-09-06
              • 1970-01-01
              相关资源
              最近更新 更多