【问题标题】:Why does creating a list from a generator object take so long?为什么从生成器对象创建列表需要这么长时间?
【发布时间】:2016-11-12 02:48:53
【问题描述】:

我对何时应该在函数中使用生成器以及何时应该只使用列表很感兴趣,因此我对过滤器和列表推导进行了一些测试。

>>> timeit.timeit('list(filter(lambda x: x%10, range(10)))')
3.281250655069016
>>> timeit.timeit('[i for i in range(10) if i%10 != 0]')
2.6070076799951494
>>> timeit.timeit('filter(lambda x: x%10, range(10))')
0.7457015149993822   

然后我尝试使用 range(100):

>>> timeit.timeit('list(filter(lambda x: x%10, range(100)))')
27.73180518404115
>>> timeit.timeit('[i for i in range(100) if i%10 != 0]')
18.659852175973356
>>> timeit.timeit('filter(lambda x: x%10, range(100))')
0.7294546449556947

为什么从生成器对象创建一个列表比简单地创建一个列表要长这么多?如果我需要多次访问该列表,使用列表推导而不是从生成器对象创建列表会更好吗?

【问题讨论】:

  • @PadraicCunningham 好吧,他们不是都得到next 被多次调用吗?但是,是的,你调用它的次数越多,完成序列迭代所需的时间就越长。
  • @Dandré, [i for i in range(10) if i%10 != 0],与列表本身相关的下一个调用在哪里?
  • @PadraicCunningham range() 是一个具有隐式调用 next 的生成器
  • 在旁注中,[i for i in range(100) if i%10 != 0] 中的!= 0 是多余的。 if i%10 够用而且速度更快。
  • @Tanmay:它不是生成器。生成器是一种非常特殊的迭代器类型,它基于暂停和恢复 Python 函数的执行,由生成器表达式或使用 yield 创建。该术语不是指任意惰性迭代。

标签: python list python-3.x generator timeit


【解决方案1】:

我将首先回答您的最后一个问题,是的,您应该使用综合列表而不是 list + filter 组合,因为它更符合 Python 风格,并且正如您所展示的那样,更有效。

至于为什么它更高效,您在理解列表中没有的filter 代码中有函数调用开销(lambda)。

这是另一个 timeit 测试向您展示:

# First the sample with list & filter, for comparison base
>>> timeit.timeit('list(filter(lambda x: x%10, range(10)))')
10.984653161001916
# Then the quickest comprehension list
>>> timeit.timeit('[i for i in range(10) if i%10]')
6.125996000002488
# And an hybrid, comprehension list using a call to previously defined lambda
>>> timeit.timeit('[i for i in range(10) if l(i)]', setup="l=lambda x: x%10")
9.257114547002857

如您所见,大部分差异来自函数调用开销。

最后 1.7 秒无法解释可能是由于 filter 弹出到 list 而理解列表将值直接构建到列表中

【讨论】:

    【解决方案2】:

    从问题中可以看出两个不同的问题

    1. 从独立过滤器调用的微小计时数据可以看出,您使用的是 Python 3.x,因为过滤器返回一个类似对象的生成器。所以理论上什么都没有发生。[1]
    2. 第一个表达式调用 lambda,这是一个在 Python 中总是代价高昂的函数调用。因此,列表推导优于调用带有内置列表的 lambda 的过滤器。[2]

    【讨论】:

      猜你喜欢
      • 2016-05-29
      • 2014-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多