Python 是不是;“收益率”-如果在“内部”中执行,则触发“退出”功能;加上;块

Python 是不是;“收益率”-如果在“内部”中执行,则触发“退出”功能;加上;块,python,Python,从带有块的内部,但是屈服如何 如果下一次调用函数,块的\uuuuu exit\uuu是否在yield上被调用,然后\uuuu enter\uuu是否再次被调用?还是等待生成器退出with块(或返回) 例如: def tmp_csv_file(): tmp_path = 'tmp.csv' with open(tmp_path, 'w+') as csv_file: yield csv_file # will this close the file? os.

从带有块的
内部,但是屈服如何

如果下一次调用函数,块的
\uuuuu exit\uuu
是否在
yield
上被调用,然后
\uuuu enter\uuu
是否再次被调用?还是等待生成器退出with块(或返回)

例如:

def tmp_csv_file():
    tmp_path = 'tmp.csv'
    with open(tmp_path, 'w+') as csv_file:
        yield csv_file # will this close the file?
    os.remove(tmp_path)

收益
中使用
插入不会触发
\uuuu退出
。发电机内部的控制流必须离开带有
块,才能触发
\uuuuuuu退出
;挂起生成器不算作将
块分开。这在精神上类似于上下文切换到另一个线程也不会触发
\uuuuuuuuuuu

让我们进行经验测试

class MyContextManager:
    def __enter__(self):
        pass
    def __exit__(self, *args):
        print "Context manager is exiting."


def f():
    print("Entered Function.")
    with MyContextManager() as m:
        print("Entered with statement. Yielding...")
        yield m
        print("Yielded. About to exit with statement.")
    print("Now outside of with statement.")

for x in f():
    pass
输出:

C:\Users\Kevin\Desktop>test.py
Entered Function.
Entered with statement. Yielding...
Yielded. About to exit with statement.
Context manager is exiting.
Now outside of with statement.

“上下文管理器正在退出”消息出现在“即将使用语句退出”消息之后,因此我们可以得出结论,
yield
语句不会触发
\uuuuuuu退出
方法。

简言之:,它将在到达
yield
语句时暂停该方法。如果您请求下一个元素,则会执行剩余部分

如果你写了
result=tmp\u csv\u file()
什么都没做:所以甚至没有执行
tmp\u path='tmp.csv'

现在,如果调用
next(result)
,Python将开始计算函数,直到它到达第一个
yield
语句。因此,它执行
tmp\u path='tmp.csv'
打开(…)
s文件并
进入环境。它点击
yield
语句,从而返回
csv\u文件
。现在,您可以对该文件执行任何操作。文件将保持打开状态(只要您不显式地
关闭()
),并且
\uuuuuuu\uuuuu
不被调用

但是,如果您第二次调用
next(result)
a,Python将继续寻找下一个
yield
语句。因此,它将退出环境中的
,并删除文件(
os.remove(tmp\u路径)
)。然后它到达方法的结尾。这意味着我们完成了。因此,
next(…)
将抛出一个错误,表示iterable已用尽。

这取决于:

  • 如果您的生成器函数超出范围(或被删除),则调用
    \uuu退出\uuu

  • 如果发电机耗尽,则调用
    \uuuu退出

=>只要生成器在with块中且未耗尽,并且保存生成器的变量未被删除,则不会调用
\uuuuu exit\uuuu

例如:

class Test(object):
    def __init__(self):
        print('init')

    def __enter__(self):
        print('enter')

    def __exit__(self, *args, **kwargs):
        print('exit')


def funfunc():
    with Test():
        yield 1
        yield 2
测试它:

>>> a = funfunc()
>>> next(a)  # no exit
init
enter
1
>>> next(a)  # no exit
2
>>> next(a, 0)  # exit because generator leaves the with-context inside the function
exit
0
或者,如果手动删除:

>>> a = funfunc()
>>> next(a)
init
enter
1
>>> del a  # exit because the variable holding the suspended generator is deleted.
exit

我会假设,发电机不会拯救他们在那个时候屈服的状态,这样他们就可以从那个点继续下去。您不使用内置的有什么原因吗?您不能自己测试一下吗(因为您显然也没有花时间阅读文档)?注意:“with”块称为上下文管理器:@暂时的狼我不知道它的存在,谢谢你的提示@马蒂诺当然可以,也一定会,不过我在上面贴了一篇文章,让其他人也能找到答案。此外,MSeifert的回答揭示了一些我不想测试的问题,因此在这里提问确实有回报:)接受了这一点,因为添加了关于删除和范围的信息,使其更完整。您不必耗尽生成器,只需使用
将其从
中推进即可。至于删除它,生成器在垃圾收集时是
close
d,但这并不总是立即发生或根本不会发生(尤其是在CPython以外的任何情况下),而且即使在
close
d时,有缺陷的生成器也可能不会退出
。@user2357112但耗尽是一种可能性(因为它相当于一个
返回None
)-即使上下文管理器没有立即退出,它最终也会在对象被销毁时退出(您是否有证据表明一个实现没有这样做,或者只是一个猜测?)。然而,您的最后一点很有趣:您所说的“错误”是什么意思未使用
?@MSeifert:、
finally
块和
退出方法退出
的生成器可能无法运行。