Skip to main content
 首页 » 编程设计

python之使用 "infinite"可迭代对象关闭循环时会发生什么

2025年05月04日15傻小

我编写了以下 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 

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

for p in primes_iterable(): 
    if p > 1000: 
        break 
    print p 

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

请您参考如下方法:

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

当你调用生成器函数时,你会得到一个可迭代的生成器对象。如果您在 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") 

这是一个示例运行:

>>> for i, s in enumerate(gen()): 
    print(s) 
    if i >= 3: 
        break 
 
starting 
foo 
foo 
foo 
foo 
caught GeneratorExit 
cleaning up