问题描述
从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耗尽。