首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >列表理解和函数函数比"for循环“更快吗?

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

Stack Overflow用户
提问于 2014-03-01 08:39:00
回答 7查看 127.5K关注 0票数 199

就Python语言的性能而言,列表理解或者像map()filter()reduce()这样的函数比for循环快吗?为什么从技术上讲,他们在C速度的中运行,而循环在python虚拟机速度中运行?

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

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2014-03-01 08:56:05

以下是基于经验的粗略指导和有根据的猜测。你应该对你的具体用例进行timeit或概要分析,以获得确切的数字,这些数字可能偶尔会与下面的数字不一致。

列表理解通常比精确等价的for循环(实际构建列表)快一点,这很可能是因为它不必在每次迭代中都查找列表及其append方法。但是,列表理解仍然执行字节码级别的循环:

代码语言:javascript
运行
复制
>>> 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)通常会稍微快一点。

假设在我正在开发的游戏中,我需要使用

循环来绘制复杂而巨大的地图。这个问题肯定是相关的,例如,如果列表理解确实更快,那么为了避免滞后(尽管代码的视觉复杂性),它将是一个更好的选择。

如果像这样的代码在用优秀的非“优化”Python编写时还不够快,那么再多的Python级别的微优化也不会让它变得足够快,您应该开始考虑使用C语言。虽然广泛的微优化通常可以显著提高Python代码的速度,但这是有一个很低的(绝对)限制的。此外,甚至在你达到这个上限之前,咬住子弹写一些C代码就会变得更具成本效益(15%的加速比300%的相同努力)。

票数 183
EN

Stack Overflow用户

发布于 2014-03-01 08:44:45

如果您检查info on python.org,您可以看到此摘要:

代码语言:javascript
运行
复制
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找到结果更快。

票数 27
EN

Stack Overflow用户

发布于 2014-03-01 08:56:49

您特别询问了map()filter()reduce(),但我假设您希望了解函数式编程的一般情况。在计算一组点中所有点之间的距离的问题上,我自己测试了这个方法,结果发现函数式编程(使用内置itertools模块中的starmap函数)比for循环(实际上需要1.25倍的时间)稍微慢一些。下面是我使用的示例代码:

代码语言:javascript
运行
复制
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

函数式版本比过程式版本快吗?

代码语言:javascript
运行
复制
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')
票数 14
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22108488

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档