当前位置: 首页 > 知识库问答 >
问题:

列表理解和函数功能是否比“for循环”更快?

隆礼骞
2023-03-14

就Python的性能而言,是列表理解,还是map()filter()duce()等函数比for循环更快?为什么从技术上讲,它们以C速度运行,而for循环以python虚拟机速度运行?

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

共有3个答案

狄元魁
2023-03-14

您特别询问了map()filter()duce(),但我假设您想了解一般的函数式编程。在自己就计算一组点内所有点之间的距离问题进行了测试后,函数式编程(使用内置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')
西门庆
2023-03-14

如果你查看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来找出结果更快。

崔单弓
2023-03-14

以下是基于经验的粗略指导和有根据的猜测。你应该timeit或者描述你的具体用例,以获得硬数字,这些数字有时可能与下面的不一致。

列表理解通常比精确等效的for循环(实际上构建了一个列表)快一点点,很可能是因为它不必在每次迭代中查找列表及其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

使用列表理解代替不构建列表的循环,毫无意义地累积一个无意义的值列表,然后扔掉该列表,由于创建和扩展列表的开销,通常速度较慢。列表理解并不是天生比一个好的循环更快的魔法。

至于函数列表处理函数:虽然这些函数是用C编写的,可能比用Python编写的等效函数性能更好,但它们不一定是最快的选项。如果函数也是用C语言编写的,那么速度会有所提高。但在大多数情况下,使用lambda(或其他Python函数),重复设置Python堆栈框架等的开销会消耗掉任何节省。简单地在线执行相同的工作,而不需要函数调用(例如,列表理解而不是mapfilter)通常会稍微快一点。

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

很有可能,如果这样的代码在用好的非“优化”Python编写时还不够快,那么无论多少Python级别的微优化都无法使其足够快,您应该开始考虑降为C。虽然广泛的微优化通常可以显着加快Python代码的速度,但这种情况的限制很低(绝对而言)。此外,即使在您达到上限之前,咬紧牙关编写一些C也会变得更具成本效益(15%的加速比与300%的加速)。

 类似资料:
  • 问题内容: 在Python的性能方面,是一个列表理解或功能,如,和比for循环快?从技术上讲,为什么它们以C速度运行,而for循环以python虚拟机速度运行? 假设在我正在开发的游戏中,我需要使用for循环绘制复杂而庞大的地图。这个问题绝对是相关的,例如,如果列表理解确实确实更快,那么它将是避免滞后的更好选择(尽管代码具有视觉复杂性)。 问题答案: 以下是粗略的准则和基于经验的有根据的猜测。你应

  • 问题内容: 以下两个循环之间的性能差异(如果有)是什么? 和 问题答案: 版本1.5中引入的for-each循环通过完全隐藏迭代器或index变量,消除了混乱和出错的机会。结果成语同样适用于集合和数组: 当你看到冒号(:)时,将其读为“ in”。因此,上面的循环读为“对于元素中的每个元素e”。请注意,即使对于数组,使用for-each循环也不会降低性能。实际上,在某些情况下,它可能只比普通的for

  • 问题内容: 以下是提供输出的代码段:。由于访问列表中的数字3,我期望获得输出。在线给出的解释是“每次迭代中更改的价值”,但我不太了解如何或为什么。任何解释都很好! 问题答案: 这里发生的是一个列表在循环过程中发生了变异。 让我们考虑以下代码片段: 输出为: 每次迭代: 从内部指针当前指向的位置读取值 立即将其分配给列表中的最后一个元素 在最后一个元素打印在标准输出上之后 就像这样: 内部指针指向第

  • 我使用以下程序集和c源(分别使用fasm和gcc)将一些程序集与一些c链接起来,以测试函数调用的成本 组件: c来源: 我得到的结果令人惊讶。首先,速度取决于我链接的顺序。如果我以的形式链接,典型的输出是 但是以相反的顺序链接,我得到了一个更像的输出: 他们的不同令人惊讶,但这不是我要问的问题。(此处有相关问题) 我要问的问题是,在第二次运行中,有函数调用的循环如何比没有函数调用的循环快,调用函数

  • 问题内容: 我想从Python中的常量列表创建lambda对象列表;例如: 但是,当我运行它们时,这将创建一个lambda对象列表: 我希望它会打印 而是打印: 似乎所有lambda都被赋予了错误的参数。我做错了什么,有办法解决吗?我认为我使用的是Python 2.4。 编辑:更多尝试的事情,例如这样: 打印从1到25的期望平方,然后使用之前的print语句: 仍然给了我全部。现有的lambda对

  • 我有一个网络应用编程接口,多个列表,和一个从网络应用编程接口查询数据的函数。我已经设法手动更新列表,一次一个。然而,数据有多个列(列表将包含列的数据),并且有38个列表并手动调用函数来更新它们是很麻烦的。我设计了一个简短的for循环来迭代列表,并相应地更新它们。但是,更新后的列表不会返回;或者甚至没有更新。如何在代码中修复此问题?请参阅下面的代码,虽然我不能提供API上的数据: =========