问题描述
从with
区域内部 ,但是如何屈服?
块的__exit__
是否会在yield
被调用,然后如果下次调用该函数,则会再次调用__enter__
?
或者是否等待生成器退出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)
1楼
让我们凭经验测试一下。
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
语句不触发__exit__
方法。
2楼
这取决于:
如果您的生成器函数超出范围(或以其他方式删除),则调用
__exit__
。如果生成器耗尽,则调用
__exit__
。
=>只要生成器处于with-block而没有耗尽,并且没有删除保存生成器的变量,则不会调用__exit__
。
例如:
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
3楼
在一个内部yield
with
不会触发__exit__
。
生成器内部的控制流必须离开with
块以便__exit__
触发;
暂停生成器并不算作离开with
块。
这在精神上类似于上下文切换到另一个线程也不会触发__exit__
。
4楼
简而言之: 不 ,它会从达到yield
语句的那一刻起暂停该方法。
如果您要求下一个元素,则执行余数。
鉴于你写了result = tmp_csv_file()
没有做任何事情 :所以甚至没有执行tmp_path='tmp.csv'
。
现在,如果你调用next(result)
,Python将开始评估函数, 直到它到达第一个yield
语句 。
所以它执行tmp_path = 'tmp.csv'
, open(..)
s文件和__enter__
是环境。
它命中yield
语句,从而返回csv_file
。
现在,您可以使用该文件执行任何操作。
该文件将保持打开状态(只要您不显式close()
它),并且不会调用 __exit__
。
不过,若你叫next(result)
第二次 ,Python将继续寻求找到下一个yield
声明 。
这将因此__exit__
的with
环境,并删除文件( os.remove(tmp_path)
然后它命中方法的结尾 。
这意味着我们已经完成了。
因此next(..)
将抛出一个错误,即iterable耗尽。