Is it safe to yield from within a “with” block in Python (and why)?

前端 未结 5 1162
轻奢々
轻奢々 2021-01-31 02:29

The combination of coroutines and resource acquisition seems like it could have some unintended (or unintuitive) consequences.

The basic question is whether or not somet

5条回答
  •  梦谈多话
    2021-01-31 03:04

    For a TLDR, look at it this way:

    with Context():
        yield 1
        pass  # explicitly do nothing *after* yield
    # exit context after explicitly doing nothing
    

    The Context ends after pass is done (i.e. nothing), pass executes after yield is done (i.e. execution resumes). So, the with ends after control is resumed at yield.

    TLDR: A with context remains held when yield releases control.


    There are actually just two rules that are relevant here:

    1. When does with release its resource?

      It does so once and directly after its block is done. The former means it does not release during a yield, as that could happen several times. The later means it does release after yield has completed.

    2. When does yield complete?

      Think of yield as a reverse call: control is passed up to a caller, not down to a called one. Similarly, yield completes when control is passed back to it, just like when a call returns control.

    Note that both with and yield are working as intended here! The point of a with lock is to protect a resource and it stays protected during a yield. You can always explicitly release this protection:

    def safe_generator():
      while True:
        with lock():
          # keep lock for critical operation
          result = protected_operation()
        # release lock before releasing control
        yield result
    

提交回复
热议问题