当前位置: 代码迷 >> python >> 如果在“with”块内完成,“yield”-ing会触发__exit__函数吗?
  详细解决方案

如果在“with”块内完成,“yield”-ing会触发__exit__函数吗?

热度:119   发布时间:2023-06-16 10:14:08.0

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)

让我们凭经验测试一下。

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__方法。

这取决于:

  • 如果您的生成器函数超出范围(或以其他方式删除),则调用__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

在一个内部yield with不会触发__exit__ 生成器内部的控制流必须离开with块以便__exit__触发; 暂停生成器并不算作离开with块。 这在精神上类似于上下文切换到另一个线程也不会触发__exit__

简而言之: ,它会从达到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耗尽。

  相关解决方案