带/不带列表理解的python函数调用

时间:2018-04-11 12:20:38

标签: python python-3.x

我有以下两个功能:

def foo(n=50000):
    return sum(i*i for i in range(n))  # just called sum() directly without 

def bar(n=50000):
    return sum([i*i for i in range(n)])  # passed constructed list to sum()

我希望foo的运行速度会比bar更快,但我已经使用%%timeit检查了ipython foo的时间稍长于bar < / p>

In [2]: %%timeit
   ...: foo(50000)
   ...: 
100 loops, best of 3: 4.22 ms per loop

In [3]: %%timeit
   ...: bar(50000)
   ...: 
100 loops, best of 3: 3.45 ms per loop
In [4]: %%timeit
   ...: foo(10000000)
   ...: 
1 loops, best of 3: 1.02 s per loop

In [5]: %%timeit
   ...: bar(10000000)
   ...: 
1 loops, best of 3: 869 ms per loop

随着我增加n的值,差异会增加,因此我尝试使用dis.dis(foo)dis.dis(bar)检查功能,但它是相同的。

那么两种方法之间存在这种时差的原因是什么?

1 个答案:

答案 0 :(得分:6)

关于发电机有很多很好的答案,所以我不会详细说明。

生成器保持状态。如果你进行快速操作(例如使用sum),它们会稍慢一些,但是如果你使用I / O命令就会有很大的不同。 生成器的好处是他们不会提前将所有项目加载到内存,列表就是这样。

当您迭代列表时(在非常高级别中)会发生这种情况:

  • 您将列表中的所有项目加载到内存
  • 要求下一个元素只是为您提供指向该对象的指针

将其与发电机进行比较:

  • 你没有记忆中的所有物品。一次一个项。
  • 请求下一个元素恢复生成器对象,运行代码直到它到达yield语句。
  • 然后它会在内存中生成对象的地址,以便您可以访问它。

中间的这个额外步骤是测试中的差异。

因此,生成器通常用于处理需要加载到内存的巨大数据量。 (对于协同程序的生成器,有更多用例)

使用大文件和for循环打印行进行过期。在某些时候,使用列表时会出现内存不足的情况。然后尝试使用发电机,他们不会失去记忆......