0

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


问题的原因:mapfunc 比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 第二代):

>>> 
3.4691164256
3.5064888507

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

>>>
5.58571625252
6.25803459664

我想我应该提到我使用的是 Python 2.7.3,所以虽然map在 Python 3 中是一个生成器,但在 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
4

5 回答 5

5

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

于 2013-04-26T13:36:33.397 回答
4

几点观察。

  • 通常这是一个坏主意[[1,1,1,1]]*n。这将创建对同一列表的n引用。这样做很好,因为它是不可变的。[None]*nNone
  • 您正在使用地图而不是循环构建列表。这引入了一些开销
  • 还有另一种选择(列表组合)

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,因此它是最干净的选择。使用mapandlist-comprehensions表示“副作用”通常不是首选做法。

于 2013-04-26T13:42:03.257 回答
2

您的翻译功能是错误的,因为它实际上返回None。而且 atom =[[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

For循环:

#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

文档

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

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

于 2013-04-26T13:43:08.223 回答
0

我无法在 Python 3.3 中复制您的结果(现在两者都map返回range惰性迭代器而不是列表):

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
于 2013-04-26T13:39:52.523 回答
0

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

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

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

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

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

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

3.8775811587063767    list comprehension
3.4751189085098315    map

另请注意,在 Python 3 中,map成为了生成器。这意味着当您向它请求更多项目时,它会延迟执行该翻译。所以这实际上是在这里更喜欢 map 的另一个原因。

于 2013-04-26T13:46:07.223 回答