Bootstrap

python 列表推导式 好处_python - 列表推导和功能函数是否比“for循环”更快?

python - 列表推导和功能函数是否比“for循环”更快?

在Python的性能方面,是一个列表理解,还是比for循环更快的map(),filter()和reduce()等函数? 从技术上讲,为什么它们“以C速度运行”,而“for循环以python虚拟机速度运行”?

假设在我正在开发的游戏中,我需要使用for循环绘制复杂且巨大的地图。 这个问题肯定是相关的,因为例如,如果列表理解确实更快,那么为了避免滞后(尽管代码的视觉复杂性),这将是一个更好的选择。

5个解决方案

102 votes

以下是基于经验的粗略指导和有根据的猜测。 您应该lambda或者描述您的具体用例以获取硬数字,这些数字可能偶尔不同意以下内容。

列表推导通常比精确等效的lambda循环(实际上构建列表)快一点,很可能是因为它不必在每次迭代时查找列表及其map方法。 但是,列表推导仍然会执行字节码级循环:

>>> dis.dis()

1 0 BUILD_LIST 0

3 LOAD_FAST 0 (.0)

>> 6 FOR_ITER 12 (to 21)

9 STORE_FAST 1 (x)

12 LOAD_FAST 1 (x)

15 LIST_APPEND 2

18 JUMP_ABSOLUTE 6

>> 21 RETURN_VALUE

使用列表推导代替不构建列表的循环,无意义地累积无意义值列表然后抛弃列表,由于创建和扩展列表的开销,因此通常较慢。 列表推导不是神奇的,本质上比一个好的旧循环更快。

至于功能列表处理功能:虽然它们是用C语言编写的,并且可能胜过用Python编写的等效函数,但它们不一定是最快的选择。 如果函数也是用C语言写的,那么预计会有一些加速。 但是大多数情况下使用lambda(或其他Python函数),重复设置Python堆栈帧等的开销会节省任何费用。 简单地在线执行相同的工作,没有函数调用(例如列表理解而不是map或filter)通常会稍快一些。

假设在我正在开发的游戏中,我需要使用for循环绘制复杂且巨大的地图。 这个问题肯定是相关的,因为例如,如果列表理解确实更快,那么为了避免滞后(尽管代码的视觉复杂性),这将是一个更好的选择。

如果像这样的代码在用非“优化”的Python编写时还不够快,那么很可能没有多少Python级别的微优化能够让它足够快,你应该开始考虑下降到C. 微优化通常可以大大加快Python代码的速度,对此的限制很低(绝对值)。 而且,甚至在你达到这个上限之前,它就变得更具成本效益(15%的加速比300%加速同样的努力)咬住子弹并写下一些C.

answered 2019-07-25T11:08:00Z

14 votes

如果你查看python.org上的信息,你可以看到这个摘要:

Version Time (seconds)

Basic loop 3.47

Eliminate dots 2.45

Local variable & no dots 1.79

Using map function 0.54

但是你真的应该详细阅读上面的文章,以了解性能差异的原因。

我还强烈建议您使用timeit来计算代码。 在一天结束时,可能存在这样的情况,例如,当满足条件时,您可能需要突破for循环。 它可能比通过调用map找到结果更快。

Anthony Kong answered 2019-07-25T11:08:39Z

12 votes

你特别询问map(),filter()和reduce(),但我想你一般都想了解函数式编程。 我自己测试了计算一组点内所有点之间距离的问题,函数式编程(使用内置itertools模块中的starmap函数)结果比for循环略慢(长度为1.25倍) , 事实上)。 这是我使用的示例代码:

import itertools, time, math, random

class Point:

def __init__(self,x,y):

self.x, self.y = x, y

point_set = (Point(0, 0), Point(0, 1), Point(0, 2), Point(0, 3))

n_points = 100

pick_val = lambda : 10 * random.random() - 5

large_set = [Point(pick_val(), pick_val()) for _ in range(n_points)]

# the distance function

f_dist = lambda x0, x1, y0, y1: math.sqrt((x0 - x1) ** 2 + (y0 - y1) ** 2)

# go through each point, get its distance from all remaining points

f_pos = lambda p1, p2: (p1.x, p2.x, p1.y, p2.y)

extract_dists = lambda x: itertools.starmap(f_dist,

itertools.starmap(f_pos,

itertools.combinations(x, 2)))

print('Distances:', list(extract_dists(point_set)))

t0_f = time.time()

list(extract_dists(large_set))

dt_f = time.time() - t0_f

功能版本比程序版本更快吗?

def extract_dists_procedural(pts):

n_pts = len(pts)

l = []

for k_p1 in range(n_pts - 1):

for k_p2 in range(k_p1, n_pts):

l.append((pts[k_p1].x - pts[k_p2].x) ** 2 +

(pts[k_p1].y - pts[k_p2].y) ** 2)

return l

t0_p = time.time()

list(extract_dists_procedural(large_set))

# using list() on the assumption that

# it eats up as much time as in the functional version

dt_p = time.time() - t0_p

f_vs_p = dt_p / dt_f

if f_vs_p >= 1.0:

print('Time benefit of functional progamming:', f_vs_p,

'times as fast for', n_points, 'points')

else:

print('Time penalty of functional programming:', 1 / f_vs_p,

'times as slow for', n_points, 'points')

andreipmbcn answered 2019-07-25T11:09:13Z

6 votes

我写了一个测试速度的简单脚本,这就是我发现的。 实际上for循环在我的情况下是最快的。 这真让我感到惊讶,请查看贝娄(正在计算平方和)。

from functools import reduce

import datetime

def time_it(func, numbers, *args):

start_t = datetime.datetime.now()

for i in range(numbers):

func(args[0])

print (datetime.datetime.now()-start_t)

def square_sum1(numbers):

return reduce(lambda sum, next: sum+next**2, numbers, 0)

def square_sum2(numbers):

a = 0

for i in numbers:

i = i**2

a += i

return a

def square_sum3(numbers):

sqrt = lambda x: x**2

return sum(map(sqrt, numbers))

def square_sum4(numbers):

return(sum([int(i)**2 for i in numbers]))

time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

0:00:00.302000 #Reduce

0:00:00.144000 #For loop

0:00:00.318000 #Map

0:00:00.390000 #List comprehension

alphiii answered 2019-07-25T11:09:46Z

3 votes

为Alphii答案添加一个扭曲,实际上for循环将是第二好的,比sum慢约6倍

from functools import reduce

import datetime

def time_it(func, numbers, *args):

start_t = datetime.datetime.now()

for i in range(numbers):

func(args[0])

print (datetime.datetime.now()-start_t)

def square_sum1(numbers):

return reduce(lambda sum, next: sum+next**2, numbers, 0)

def square_sum2(numbers):

a = 0

for i in numbers:

a += i**2

return a

def square_sum3(numbers):

a = 0

map(lambda x: a+x**2, numbers)

return a

def square_sum4(numbers):

a = 0

return [a+i**2 for i in numbers]

time_it(square_sum1, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum2, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum3, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

time_it(square_sum4, 100000, [1, 2, 5, 3, 1, 2, 5, 3])

主要的变化是消除了缓慢的sum调用,以及在最后一种情况下可能不必要的int()。 实际上,将for循环和地图放在相同的术语中是非常事实。 请记住,lambdas是功能概念,理论上不应该有副作用,但是,它们可能有副作用,如添加到a。这种情况下的结果是Python 3.6.1,Ubuntu 14.04,Intel(R)Core(TM)i7-4770 CPU @ 3.40GHz

0:00:00.257703

0:00:00.184898

0:00:00.031718

0:00:00.212699

jjmerelo answered 2019-07-25T11:10:20Z

;