Python中的GeneratorExit功能

2024-05-19 12:04:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我写了一个关于Python生成器的测试程序。但我犯了一个意想不到的错误。我不知道该怎么解释。让我给你看看密码:

def countdown(n):
    logging.debug("Counting down")
    while n > 0:
        try:
            yield n
        except GeneratorExit:
            logging.error("GeneratorExit")
        n -= 1


if __name__ == '__main__':
    c = countdown(10)
    logging.debug("value: %d", c.next())

我认为它应该运行没有任何问题。但结果是:

# ./test.py
[2015/06/16 04:10:49] DEBUG    - Counting down     
[2015/06/16 04:10:49] DEBUG    - value: 10 
[2015/06/16 04:10:49] ERROR    - GeneratorExit
Exception RuntimeError: 'generator ignored GeneratorExit' in <generator object countdown at 0x7f9934407640> ignored

为什么最后一行有错误。我不知道为什么我触发了GeneratorExit异常。除了发电机我还有什么遗漏吗?我还将代码输入到交互式python shell中,一切正常。怎么会这样?


Tags: debug密码valueloggingdef错误generator测试程序
3条回答

当生成器对象在程序结束时被垃圾收集时,将调用其close()方法,这将在生成器中引发GeneratorExit异常。通常,这不会被捕获并导致发电机退出。

由于捕捉到异常并继续产生另一个值,这将导致RuntimeError。如果捕捉到GeneratorExit异常,则需要重新引发该异常,或者退出该函数而不产生任何其他异常。

可能是因为您的屈服值n在try块中,try块总是返回新的n引用,这使得最后的n值被自动垃圾收集。它也是PEP 342中的状态:

"Add support to ensure that close() is called when a generator iterator is garbage-collected"

"Allow yield to be used in try/finally blocks, since garbage collection or an explicit close() call would now allow the finally clause to execute."

由于生成器中的close方法相当于抛出GeneratorExit并被您的异常捕获,因此将执行logging.error('GeneratorExit')表达式。

“RunTimeError”是由于生成器产生下一个值n(9)而引发的,它是python文档中的状态https://docs.python.org/3.6/reference/expressions.html#generator-iterator-methods

"Raises a GeneratorExit at the point where the generator function was paused. If the generator function then exits gracefully, is already closed, or raises GeneratorExit (by not catching the exception), close returns to its caller. If the generator yields a value, a RuntimeError is raised. If the generator raises any other exception, it is propagated to the caller. close() does nothing if the generator has already exited due to an exception or normal exit"

可能是这样的代码:

#pygen.py

import sys
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
        %(levelname)s - %(message)s', datefmt='[%Y/%m/%d %H:%M:%S]')

def genwrapper(func):
    #makes gen wrapper
    #which automatically send(None) to generator
    def wrapper(n=None):
        f = func(n)
        f.send(None)
        return f
    return wrapper

@genwrapper
def countdown(n=None):
    logging.debug('Counting Down')
    while True:
        try:
            n = yield(n)
        except GeneratorExit as e:
            logging.error('GeneratorExit')
            raise e

if __name__ == '__main__':
    n = int(sys.argv[1])
    c = countdown() #avoid function call in loop block (avoid new reference to c)
     while n > 0:
         a = c.send(n)
         logging.debug('Value: %d', a)
         n -= 1

然后在你的终端:

guest@xxxxpc:~$ python pygen.py 5

将导致:

[2018/12/13 16:50:45]         DEBUG - Counting Down
[2018/12/13 16:50:45]         DEBUG - Value: 5
[2018/12/13 16:50:45]         DEBUG - Value: 4
[2018/12/13 16:50:45]         DEBUG - Value: 3
[2018/12/13 16:50:45]         DEBUG - Value: 2
[2018/12/13 16:50:45]         DEBUG - Value: 1
[2018/12/13 16:50:45]         ERROR - GeneratorExit

很抱歉我的英语不好或我的建议不够清楚,谢谢

假设您有以下生成器:

def gen():
    with open('important_file') as f:
        for line in f:
            yield line

你只要next一次就把它扔掉:

g = gen()
next(g)
del g

生成器的控制流从不离开with块,因此文件不会被关闭。为了防止这种情况,当垃圾收集生成器时,Python调用其close方法,该方法在生成器最后一次运行时引发GeneratorExit异常。此异常旨在触发任何没有机会运行的finally块或上下文管理器__exit__

当您捕获GeneratorExit并继续运行时,Python会看到生成器没有正确退出。因为这可能表示资源没有正确释放,所以Python将其报告为运行时错误。

相关问题 更多 >

    热门问题