Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what does yield without value do in context manager

import contextlib import time  @contextlib.contextmanager def time_print(task_name):     t = time.time()     try:         yield     finally:         print task_name, "took", time.time() - t, "seconds."   def doproc():     x=1+1   with time_print("processes"):     [doproc() for _ in range(500)]  # processes took 15.236166954 seconds. 

when does doproc get executed when using this decorator?

like image 719
Shuman Avatar asked Feb 18 '16 18:02

Shuman


People also ask

What actions are guaranteed by a context manager?

Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. Suppose you have two related operations which you'd like to execute as a pair, with a block of code in between.

How does yield work in Python?

What Is Yield In Python? The Yield keyword in Python is similar to a return statement used for returning values or objects in Python. However, there is a slight difference. The yield statement returns a generator object to the one who calls the function which contains yield, instead of simply returning a value.

Does yield stop execution Python?

The main difference between them is: In python the return statement stops the execution of the function. Whereas, the yield statement only pauses the execution of the function.

What does the context manager do when you are opening a file using with?

A context manager usually takes care of setting up some resource, e.g. opening a connection, and automatically handles the clean up when we are done with it. Probably, the most common use case is opening a file. The code above will open the file and will keep it open until we are out of the with statement.


1 Answers

yield expression returns control to the whatever is using the generator. The generator pauses at this point, which means that the @contextmanager decorator knows that the code is done with the setup part.

In other words, everything you want to do in the context manager __enter__ phase has to take place before the yield.

Once your context exits (so the block under the with statement is done), the @contextmanager decorator is called for the __exit__ part of the context manager protocol and will do one of two things:

  • If there was no exception, it'll resume your generator. So your generator unpauses at the yield line, and you enter the cleanup phase, the part

  • If there was an exception, the decorator uses generator.throw() to raise that exception in the generator. It'll be as if the yield line caused that exception. Because you have a finally clause, it'll be executed before your generator exits because of the exception.

So, in your specific example the sequence is as follows:

  1. with time_print("processes"):

    This creates the context manager and calls __enter__ on that.

  2. The generator starts execution, t = time.time() is run.

  3. The yieldexpression pauses the generator, control goes back to the decorator. This takes whatever was yielded and returns that to the with statement, in case there is an as target part. Here None is yielded (there is only a plain yield expression).

  4. [doproc() for _ in range(500)] is run and completes.

  5. The context manager __exit__ method is run, no exception is passed in.

  6. The decorator resumes the generator, it continues where it left off.

  7. The finally: block is entered and print task_name, "took", time.time() - t, "seconds." is executed.

  8. The generator exits, the decorator __exit__ method exits, all is done.

like image 181
Martijn Pieters Avatar answered Oct 08 '22 05:10

Martijn Pieters