2016-02-18 4 views
5
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. 

Когда dooproc выполняется при использовании этого декоратора?Что делает результат без значения в контекстном менеджере

+0

[Из документов:] (https://docs.python.org/2/library/contextlib.html#contextlib.contextmanager) «В тот момент, когда генерируется генератор, выполняется блок, вложенный в оператор« с ». Затем генератор возобновляется после выхода блока. Если в блоке возникает необработанное исключение, оно ререйзируется внутри генератора в точке где произошел доход ». –

+0

'yield' без аргумента семантически эквивалентен' yield None' –

ответ

7

yield выражение возвращает управление тому, что используется генератором. Генератор делает паузу в этом пункте, а это значит, что декоратор @contextmanager знает, что код выполнен с помощью установки.

Другими словами, все, что вы хотите сделать в менеджере контекста __enter__, должно пройти до yield.

После того, как ваш контекст выходит (поэтому блок под with заявление сделано), то @contextmanager декоратора вызывается для __exit__ части протокола контекста менеджера и сделать одну из двух вещей:

  • Если не было никакого исключения, он возобновит генератор. Так что ваш генератор продолжает воспроизведение на yield линии, и вы вступаете в фазу очистки, часть

  • Если есть исключение, декоратор использует generator.throw() поднять это исключение в генераторе. Это будет как если бы строка yield вызвала это исключение. Поскольку у вас есть предложение finally, оно будет выполнено до того, как ваш генератор выйдет из-за исключения.

Таким образом, в вашем конкретном примере последовательность выглядит следующим образом:

  1. with time_print("processes"):

    Это создает менеджер контекста и вызывает __enter__ по этому вопросу.

  2. Генератор запускает выполнение, запускается t = time.time().

  3. Выражение выражения yield приостанавливает генератор, управление возвращается к декоратору. Это берет то, что было получено, и возвращает его в инструкцию with, если есть часть as target.

  4. [doproc() for _ in range(500)] запущен и завершен.

  5. Контекст менеджер __exit__ метод запускается, исключение не передается в.

  6. Декоратор возобновляет генератор, он по-прежнему где она была прервана.

  7. Введено блок finally: и выполнен print task_name, "took", time.time() - t, "seconds.".

  8. Генератор выходит, декоратор __exit__ метод выхода, все сделано.