列表理解和function函数比“for循环”更快吗?

就Python的性能而言,它是一个列表理解,还是像map(),filter()和reduce()这样的函数比for循环更快呢? 为什么在技术上,他们“以C速度运行”,而“for循环以Python虚拟机速度运行”?

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

以下是基于经验的粗略指导和教育猜测。 你应该对你的具体使用情况进行timeit或简介,以获得硬数字,而这些数字偶尔会与下面的数字不一致。

列表理解通常比精确等价for循环(实际上构build列表)要快一点点,这很可能是因为它不必在每次迭代中查找列表和append方法。 然而,列表理解仍然是一个字节码级的循环:

 >>> dis.dis(<the code object for `[x for x in range(10)]`>) 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 

使用列表理解来替代构build列表的循环,无意义地累积无意义值的列表,然后将列表抛出,通常由于创build和扩展列表的开销而变慢 。 列表理解本身并不比旧的循环更快。

至于函数列表处理函数:虽然这些函数是用C语言编写的,而且可能比用Python编写的等效函数要好,但它们不一定是最快的select。 如果函数是用C语言写的, 那么预计会加速一些。 但大多数情况下使用lambda (或其他Python函数),重复设置Python堆栈帧等的开销会节省任何费用。 简单地做同样的工作,没有函数调用(例如列表理解,而不是mapfilter )通常稍微快一点。

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

很有可能,如果这样的代码在编写好的非“优化的”Python时速度还不够快,那么没有任何一个Python级别的微型优化可以使它足够快,你应该开始考虑下降到C。微型优化通常可以大大加快Python代码的速度,但对此有一个很低的(绝对值)限制。 而且,即使在你达到天花板之前,它也变得更加经济实惠(15%的速度提升,300%的提速,同样的努力)咬下子弹,写出一些C.

如果你检查python.org上的信息 ,你可以看到这个总结:

 Version Time (seconds) Basic loop 3.47 Eliminate dots 2.45 Local variable & no dots 1.79 Using map function 0.54 

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

我也强烈build议你应该使用timeit来计算你的代码。 在一天结束的时候,可能会出现这样的情况,例如,当条件满足时,您可能需要跳出for循环。 通过调用map查找结果可能会更快。

你特别要求map(​​),filter()和reduce(),但是我想你一般都想知道函数式编程。 在testing了一组点之间所有点之间距离的问题之后,函数式编程(使用内置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 

function版本比程序版本更快吗?

 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') 

我写了一个简单的脚本来testing速度,这是我发现的。 其实循环在我的情况是最快的。 这真的让我感到惊讶,看看下面(正在计算平方和)。

 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