为什么列表理解中的lambda函数比map中的慢?

2024-04-25 19:41:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我做了个测试

%timeit list(map(lambda x:x,range(100000)))
%timeit [(lambda x:x)(i) for i in range(100000)]

给予

29 ms ± 4.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
51.2 ms ± 3.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

为什么python中的lambda函数在列表理解上比在map中慢


Tags: oflambdadevloopmaprunsrangemean
2条回答

TLDR:理解每次都会评估其整个表达式。Amap对其表达式求值一次,并每次应用结果


输入到map的表达式和理解的处理方式不同。 map和理解的重要部分可以用以下方式翻译:

def map(func: 'callable', iterable):
    for item in iterable:
        yield func(item)

def comprehension(expr: 'code', iterable):
    for item in iterable:
        yield eval(expr)

重要的区别在于map已经接收到函数,而comprehension接收到表达式

现在,lambda x:x是一个表达式,的计算结果是一个函数

>>> co = compile('lambda x: x', '<stackoverflow>', 'eval')
>>> co
<code object <module> at 0x105d1c810, file "<stackoverflow>", line 1>
>>> eval(co)
<function __main__.<lambda>(x)>

值得注意的是,计算表达式以获得函数是一项需要时间的操作

因为map和理解期望不同的事物,所以lambda传递给它们的方式不同。解释器的功能可以通过显式eval/compile扩展:

>>> list(map(eval(compile('lambda x: x', '<stackoverflow>', 'eval')), range(10)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(comprehension(compile('(lambda x: x)(item)', '<stackoverflow>', 'eval'), range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

重要的区别现在应该清楚了:

  • 对于map,表达式在传递到map之前被计算一次。对于每个项,map调用结果函数
  • 对于comprehension,表达式以未赋值的形式传递。对于每个项,comprehension构造函数,然后调用结果函数

重要的是要注意map并不总是比理解快map创建/查找一次函数会带来好处。然而,函数调用是昂贵的,理解不必使用函数

通常,如果表达式只使用理解的局部名称,则速度更快

In [302]: %timeit list(map(lambda i:i,range(100000)))
     ...: %timeit [i for i in range(100000)]
8.86 ms ± 38.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
4.49 ms ± 35.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

如果表达式本身需要动态地执行代价高昂的操作,map将不会受益

In [302]: %timeit list(map(lambda i: (lambda x:x)(i),range(100000)))
     ...: %timeit [(lambda x:x)(i) for i in range(100000)]
19.7 ms ± 39.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
15.9 ms ± 43.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

因为在第一个代码段中,lambda只创建一次并执行100000次,而在第二个代码段中,它在每次迭代中都创建并执行

老实说,我感到惊讶的是,两者之间的差异并没有更大,但你们的两个计时应该会进一步分开,因为它的长度最大


作为旁注,请注意,即使将lambda更改为不必首先创建而只需查找的内置函数,也会得到相同的趋势:

> py -m timeit "list(map(float, range(10000)))"
200 loops, best of 5: 1.78 msec per loop

> py -m timeit "[float(x) for x in range(10000)]"
100 loops, best of 5: 2.35 msec per loop

可以通过将函数绑定到变量来实现改进,但是list(map())方案速度更快

> py -m timeit "r=float;[r(x) for x in range(10000)]"
100 loops, best of 5: 1.93 msec per loop

至于为什么到局部变量的绑定更快,您可以找到更多信息here

相关问题 更多 >