请考虑使用Python 2.x代码段。
from __future__ import print_function
class myfile(file):
def __exit__(self, *excinfo):
print("__exit__ called")
super(myfile, self).__exit__(*excinfo)
def my_generator(file_name):
with myfile(file_name) as fh:
for line in fh:
yield line.strip()
gen = my_generator('file.txt')
print(next(gen))
print("Before del")
del gen
print("After del")
此脚本的输出(给定file.txt有多行)是:
Line 1 from file
Before del
__exit__ called
After del
我对__exit__
专门致电感兴趣。
是什么触发了他的方法的执行?对于我们所知道的,代码从未离开with
语句(它在yield
语句之后“停止”并且从未继续)。当发电机的参考计数降至0时,是否可以保证将调用__exit__
答案 0 :(得分:4)
在回收生成器对象时,Python调用其close
方法,如果它还没有完成执行,则在其GeneratorExit
的最后yield
点处引发GeneratorExit
异常。当此__exit__
传播时,它会触发您使用的上下文管理器的yield
方法。
这是在Python 2.5中引入的,in the same PEP as send
and yield expressions。在此之前,你不能try
finally
with
内yield
,如果DBI:mysql:database=$database;host=$hostname;port=$port
语句已存在于2.5之前,你就不会有能够host
里面的任何一个。
答案 1 :(得分:1)
要添加到@user2357112's answer,with
块会在其中引发异常时中断。此异常将传递给为上下文创建的对象的__exit__
方法。
file
类似乎无声地传递GeneratorExit
异常,因为没有任何信号表明它。但是,如果您在argc
方法中打印myfile.__exit__
,则会看到上下文未自然关闭:
class myfile(file):
def __exit__(self, *excinfo):
print("__exit__ called")
print(excinfo[0]) # Print the reason why the context exited
super(myfile, self).__exit__(*excinfo)
您的脚本输出:
Line 1 from file
Before del
__exit__ called
<type 'exceptions.GeneratorExit'>
After del