代码之家  ›  专栏  ›  技术社区  ›  Derek Litz

什么时候应该使用映射而不是for循环?

  •  39
  • Derek Litz  · 技术社区  · 15 年前

    这与以下内容有关:(在python代码中)

    for i in object:
         doSomething(i)
    

    对战

    map(doSomething, object)
    

    两者都很容易理解,而且都很短,但是有什么速度差吗?现在,如果dosometing有一个返回值,我们需要检查它,它将作为映射的列表返回,在for循环中,我们可以创建自己的列表,也可以一次检查一个列表。

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

    对战

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

    现在,我觉得两者有点分歧。两个具有返回值功能的剂量测量值可能会有所不同,这取决于我们在循环过程中是在动态检查它们,还是在最后一次检查它们是否会产生不同的结果。而且,似乎for循环总是可以工作的,可能更慢一些,因为映射只在某些场景下工作。当然,我们可以通过扭曲来实现这两种效果,但关键是要避免这种类型的工作。

    我要寻找的是一个场景,在性能、可读性、可维护性或实现速度方面,映射函数与一个做得很好的for循环相比,确实非常出色。如果答案是真的没有什么大的区别,那么我想知道人们在实践中何时使用一个或另一个,或者它是否真的是完全武断的,并且由编码标准来设置,这取决于你的机构。

    谢谢!

    4 回复  |  直到 7 年前
        1
  •  26
  •   Don    7 年前

    map 当您要将函数应用于ITerable的每个项并返回结果列表时,此函数非常有用。这比使用for循环和构造列表更简单、更简洁。

    for 在其他情况下更容易阅读,并且在Lisp中有许多迭代构造基本上是使用宏和映射编写的。所以,如果 地图 不适合,使用 对于 循环。

    理论上,如果我们有一个足够聪明的编译器/解释器来使用多个CPU/处理器,那么 地图 可以更快地实现,因为每个项目的不同操作可以并行完成。不过,我认为目前情况并非如此。

        2
  •  10
  •   Mark Tolonen    15 年前

    您熟悉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中,使用没有函数调用的列表comp。

        3
  •  8
  •   SilentGhost    15 年前

    只是使用列表理解:它们更像是蟒蛇。它们的语法也类似于生成器表达式,这使得从一个表达式切换到另一个表达式更容易。将代码转换为py3k时不需要更改任何内容: map 返回py3k中的iterable,您必须调整代码。

    如果您不关心返回值,只需不命名新列表,就需要在代码中使用返回值一次,您可以切换到生成器表达式,并在结尾处对单个列表进行理解。

        4
  •  -1
  •   iamamac    15 年前

    编辑 :我没有意识到 map 等于 itertools.imap 在python 3.0之后。所以这里的结论可能不正确。明天我将在python 2.6上重新运行测试并发布结果。

    如果 doSomething 非常“微小”, 地图 可能比 for 循环或列表理解:

    # Python 3.1.1 (r311:74483, Aug 17 2009, 17:02:12) [MSC v.1500 32 bit (Intel)] on win32
    
    from timeit import timeit
    
    do = lambda i: i+1
    
    def _for():
      for i in range(1000):
        do(i)
    
    def _map():
      map(do, range(1000))
    
    def _list():
      [do(i) for i in range(1000)]
    
    timeit(_for, number=10000)  # 2.5515936921388516
    timeit(_map, number=10000)  # 0.010167432629884843
    timeit(_list, number=10000) # 3.090125159839033
    

    这是因为 地图 是用C写的,而 对于 循环和列表理解在Python虚拟机中运行。