首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >与列表相比,在生成器上迭代多次的速度

与列表相比,在生成器上迭代多次的速度
EN

Stack Overflow用户
提问于 2016-11-25 14:34:42
回答 3查看 2.1K关注 0票数 4

我预计在多个循环的情况下,列表迭代要比使用生成器快得多,我的代码表明这是错误的。

我的理解是(操作是指定义元素的任何表达式):

  • 列表需要初始化n个操作。
  • 但是,列表上的每一个循环都只是从内存中获取一个元素。
  • 因此,列表上的m循环只需要n个操作。
  • 生成器不需要初始化任何操作。
  • 然而,循环遍历生成器运行的是动态操作。
  • 因此,生成器上的一个循环需要n个操作。
  • 但是生成器上的m循环需要n×m运算。

我使用以下代码检查了我的期望:

代码语言:javascript
运行
复制
from timeit import timeit

def pow2_list(n):
    """Return a list with powers of 2"""

    results = []

    for i in range(n):
        results.append(2**i)

    return results

def pow2_gen(n):
    """Generator of powers of 2"""

    for i in range(n):
        yield 2**i

def loop(iterator, n=1000):
    """Loop n times over iterable object"""

    for _ in range(n):
        for _ in iterator:
            pass

l = pow2_list(1000) # point to a list
g = pow2_gen(1000)  # point to a generator


time_list = \
    timeit("loop(l)", setup="from __main__ import loop, l", number=10)

time_gen = \
    timeit("loop(g)", setup="from __main__ import loop, g", number=10)

print("Loops over list took: ", time_list)
print("Loops over generator took: ", time_gen)

结果让我吃惊..。

代码语言:javascript
运行
复制
Loops over list took:  0.20484769299946493
Loops over generator took:  0.0019217690005461918

不知何故,即使在循环1000次以上时,使用生成器的速度也比列表快得多。在这种情况下,我们讨论的是两个数量级!为什么?

编辑:

谢谢你的回答。现在我看到了我的错误。我错误地认为生成器从一个新的循环开始,就像range:

代码语言:javascript
运行
复制
>>> x = range(10)
>>> sum(x)
45
>>> sum(x)
45

但这太天真了(range不是发电机.)

关于可能重复的注释:我的问题涉及生成器上的多个循环,这在另一个线程中没有解释。

EN

Stack Overflow用户

发布于 2016-11-25 15:06:13

你的考试有问题。也就是说,生成器是不可重用的。一旦筋疲力尽,就不能再使用它,必须生成一个新的。例如:

代码语言:javascript
运行
复制
l = [0, 1, 2, 4, 5]
g = iter(l) # creates an iterator (a type of generator) over the list

sum_list0 = sum(l)
sum_list1 = sum(1)
assert sum_list0 == sum_list1 # all working normally

sum_gen0 = sum(g) # consumes generator
sum_gen1 = sum(g) # sum of empty generator is 0
assert sum_gen0 == sum_list1 # result is correct
assert sum_gen1 == sum_list1, "second result was incorrect" # because generator was exhausted

要使测试正常运行,必须在传递给timeit的语句中重新创建生成器。

代码语言:javascript
运行
复制
from timeit import timeit

n = 1000
repeats = 10000

list_powers = [2**i for i in range(n)]
def gen_powers():
    for i in range(n):
        yield 2**i

time_list = timeit("min(list_powers)", globals=globals(), number=repeats)
time_gen = timeit("min(gen_powers())", globals=globals(), number=repeats)

print("Loops over list took: ", time_list)
print("Loops over generator took: ", time_gen)

给予:

代码语言:javascript
运行
复制
Loops over list took:  0.24689035064701784
Loops over generator took:  13.551637053904571

现在,发生器比列表慢两个数量级。这是预期的,因为与序列上的迭代次数相比,序列的大小很小。如果n很大,那么列表的创建就会变慢。这是因为在追加新项时如何展开列表,以及创建时未传递给列表的最终大小。与生成器相比,增加迭代次数将加快列表的速度,因为生成器所需的工作量增加,而对于列表,则保持不变。由于n只有1000 (小),而repeatsn的主导地位,那么生成器就会慢一些。

票数 4
EN
查看全部 3 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40807277

复制
相关文章

相似问题

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