使用“无限”迭代器关闭循环时会发生什么?

Dan*_*oud 1 python

我已经编写了以下python函数:

import numpy

def primes_iterable():
    """Iterable giving the primes"""
    # The lowest primes
    primes = [2,3,5]
    for p in primes:
        yield p
    for n in potential_primes():
        m = int(numpy.sqrt(n))
        check = True
        for p in primes:
            if p > m:
                break
            if n%p == 0:
                check = False
        if check:
            primes.append(n)
            yield n

def potential_primes():
    """Iterable starting at 7 and giving back the non-multiples of 2,3,5"""
    yield 7
    n = 7
    gaps = [4,2,4,2,4,6,2,6]
    while 1:
        for g in gaps:
            n += g
            yield n
Run Code Online (Sandbox Code Playgroud)

如您所见,这两个函数都没有return语句。假设我要写这样的东西:

for p in primes_iterable():
    if p > 1000:
        break
    print p
Run Code Online (Sandbox Code Playgroud)

break到达语句后,在内存级别会发生什么?如果我理解正确,调用primes_iterable()会使函数启动,直到下一个函数yield再暂停直到再次需要它为止。break到达该语句时,该函数实例是否关闭,或者它继续存在于后台,完全没有用?

Blc*_*ght 5

您的函数primes_iterable是生成器函数。调用它时,不会立即发生任何事情(除了返回生成器对象之外)。只有在next被调用时,它才会运行到下一个yield

调用generator函数时,将获得一个可迭代的generator对象。如果在for循环中执行此操作,则循环将在运行时保留对生成器对象的引用。如果您break不在循环中,则该引用将被释放,并且可以对垃圾回收器生成器对象进行收集。

但是,当清理生成器对象时,在生成器函数中运行的代码会如何处理?它被暂停时GeneratorStop抛出的异常中断yield。如果需要,可以让您的生成器函数捕获此异常,但是除了清理资源并退出之外,您无济于事。通常是使用try/ finally对而不是except语句来完成。

这是一些示例代码,演示了此行为:

def gen():
    print("starting")
    try:
        while 1:
            yield "foo"
    except GeneratorExit:
        print("caught GeneratorExit")
        raise
    finally:
        print("cleaning up")
Run Code Online (Sandbox Code Playgroud)

这是一个示例运行:

>>> for i, s in enumerate(gen()):
    print(s)
    if i >= 3:
        break

starting
foo
foo
foo
foo
caught GeneratorExit
cleaning up
Run Code Online (Sandbox Code Playgroud)