在某些情况下,for循环比map更快吗?

时间:2013-04-26 13:32:59

标签: python map

问题实际上是标题所说的。

问题的原因:
map func比下面代码中的for循环慢 是因为我的代码中存在某些问题,还是还有其他问题?

代码:

import timeit

setup = '''
def translate(x):
    x[1]+=1
    x[2]+=1
    x[3]+=1

atoms = [[1,1,1,1]]*1000
'''
smt1 = '''for i in atoms: translate(i)'''
smt2 = '''map(translate, atoms)'''

time_for = timeit.Timer(setup=setup, stmt=smt1)
time_map = timeit.Timer(setup=setup, stmt=smt2)

print time_for.timeit(10000)
print time_map.timeit(10000)

输出(Windows 7(64位)I-3 2nd gen):

>>> 
3.4691164256
3.5064888507

输出(Windows 7(32位)core2duo):

>>>
5.58571625252
6.25803459664

我想我应该提到我使用的是Python 2.7.3,所以尽管Python 3中的map是一个生成器,但在Python 2中却不是这样,所以这个“问题”无法复制到Python 3。


更新

为了解决那些说原子应该是不可变的,这里有一个不同版本的设置(它更慢,但仍然显示差异):

setup = '''
def translate(x):
    return tuple(i+1 for i in x)

atoms = [(1,1,1,1)]*1000
'''

输出(Windows 7(32位)core2duo):

>>> 
31.0213507144
29.7194933508

5 个答案:

答案 0 :(得分:5)

这是因为map创建了一个新结构,而for只修改了当前结构。

答案 1 :(得分:4)

一些观察结果。

  • 通常做[[1,1,1,1]]*n是个坏主意。这会创建对{em>相同列表的n个引用。 [None]*n虽然None是不可变的,但这样做很好。
  • 您正在构建包含地图但不包含循环的列表。这引入了一些开销
  • 还有另一个选项(list comp)

import timeit

setup = '''
def translate(x):
    x[1]+=1
    x[2]+=1
    x[3]+=1

atoms = [[1,1,1,1] for _ in range(1000) ]
'''
smt1 = '''lst = []
for i in atoms: lst.append(translate(i))'''
smt2 = '''map(translate, atoms)'''
smt3 = '''[translate(i) for i in atoms]'''

time_for = timeit.Timer(setup=setup, stmt=smt1)
time_map = timeit.Timer(setup=setup, stmt=smt2)
time_lc  = timeit.Timer(setup=setup, stmt=smt3)

print time_for.timeit(10000)
print time_map.timeit(10000)
print time_lc.timeit(10000)

随着说。 map对我来说仍然较慢

7.49916100502
7.83171486855
6.13082003593

并且列表理解胜出手。

然而,作为一种风格,我肯定会在这里使用for循环。由于您没有从translate返回任何内容,因此它是最干净的选择。使用maplist-comprehensions进行“副作用”通常不是首选做法。

答案 2 :(得分:2)

您的翻译功能错误,因为它实际返回None。原子= [[1,1,1,1]]*1000只不过是同一物体的副本1000次。

这是我的时间结果:

In [49]: def translate(x):
    x[:3]=[y+1 for y in x[:3]]
    return x
   ....: 
In [54]: lis = [[1,1,1,1] for _ in xrange(10**5)]

使用map()

In [55]: %timeit map(translate,lis)
1 loops, best of 3: 151 ms per loop

有关环:

#this is fast as no list is created in this, i.e you're not storing the 
#returned value anywhere. So technically it's not equivalent to a LC or `map`.

In [56]: %timeit for i in lis: translate(i)  
1 loops, best of 3: 146 ms per loop

列表理解:

In [57]: %timeit [translate(i) for i in lis]
1 loops, best of 3: 153 ms per loop

来自docs

  

Python支持几个循环结构。 for语句是   最常用的。它循环遍历序列的元素,   将每个分配给循环变量。如果你的循环体是   简单来说,for循环本身的解释器开销可以是一个   大量的开销。这是map函数的位置   很方便。您可以将地图视为移动到C代码中。唯一的   限制是map的“循环体”必须是函数调用。   除了列表推导的句法优势之外,它们通常比等效使用地图更快或更快。

因此,在大多数情况下map仅在与built-in函数一起使用时才优于列表推导。

答案 3 :(得分:0)

我无法在Python 3.3中复制您的结果(maprange现在都返回 lazy iterator 而不是列表):

In [7]: %timeit list(map(lambda x: x + 1, range(1000)))
1000 loops, best of 3: 218 us per loop

In [8]: %timeit [x + 1 for x in range(1000)]
10000 loops, best of 3: 99.5 us per loop

答案 4 :(得分:0)

请注意,您的两项操作会做不同的事情。

smt1遍历您的列表,翻译每个项目并丢弃结果。 smt2实际上翻译所有元素并返回包含所有已更改对象的列表。

因此,地图实际上比第一个语句更复杂,只是因为它存储了所有已翻译的项目。

您可以将第一个语句更改为列表推导,以使其返回相同的结果:

smt1 = '''[translate(i) for i in atoms]'''

当我这样做时,我得到以下结果:

3.8775811587063767    list comprehension
3.4751189085098315    map

另请注意,在Python 3中,map成为了一个生成器。这意味着当您从中请求更多项目时, lazily 执行该翻译。所以这是在这里更喜欢地图的另一个原因。