Найти - Пользователи
Полная версия: Зачем нужен декоратор @contextmanager ?
Начало » Python для экспертов » Зачем нужен декоратор @contextmanager ?
1
plusplus
Не могу понять какой от него толк, если внутри него всё равно необходимо писать try…finally?
import contextlib
@contextlib.contextmanager
def make_context():
    print '  entering'
    try:
        yield {}
    except RuntimeError, err:
        print '  ERROR:', err
    finally:
        print '  exiting'

И наоборот, если не писать:
import contextlib
@contextlib.contextmanager
def make_context(name):
    print 'entering:', name
    yield name
    print 'exiting :', name
with make_context('something'):
     print 'middle'
     raise Exception('something')
то, если сработает исключение, код print ‘exiting’ не выполнится. В чем тогда смысл данной конструкции, зачем она нужна?
Lexander
Потом пишем:
with make_context(name):
    do_some_work_with_that_name()
и гарантированно печатаем ‘exiting’

Полезно, если внутри make_context выполняется сразу несколько операций, в процессе которых возможны проблемы и нам нужно вернуть все на начало вызова функции.

Например, внутри работа с временными файлами.
Сделал дело и почистил после себя.
plusplus
Lexander
и гарантированно печатаем ‘exiting’

Дак если бы, но не пишет оно, вот код:

import contextlib
@contextlib.contextmanager
def make_context(name):
    print 'entering:', name
    yield name
    print 'exiting :', name
with make_context('something'):
     print 'middle'
     raise Exception('something')

У меня не пишет, ЧЯДНТ?
sergeek
Оно с исключениями никак не связано. Это используют для того чтобы повысить читабельность в случаях когда нужно что-то выполнить перед вступлением и перед выходом из контекста
как тут вон
Lexander
plusplus
У меня не пишет, ЧЯДНТ?
В этом примере выход идет раньше на строчке с yield и с исключениями это действительно не связано.
Lexander
Давайте зайдем с другого конца.
Менеджер контекста, создаваемый с помощью with нужен для правильного завершения операций внутри контекста.
Так и с @contextlib.contextmanager.
Использовать его нужно именно в этих случаях, когда нужно гарантировать, чтобы вне зависимости от ошибок или исключений внутри создаваемого контекста, после выхода из контекста состояние программы было предсказуемым.

Пример.

Нужно собрать заархивированные данные последовательно из нескольких источников достаточно большого объема и слить их вместе, обработать и удалить все временные данные.
По одному файлу для каждого источника.

Создаем контекст.
Запускаем внутри функцию, которая сохраняет архивы в temp, там же их распаковывает и сливает в один файл.
Потом этот файл копируем куда нам нужно.
Так как во время процедуры работы с файлами могут возникнуть исключения, то нам нужно гарантировать удаления всех созданных нами временных файлов из temp.
Вот именно этот код удаления мы и поместим в finally созданного с помощью @contextlib.contextmanager контекста.
bw
Отвечая на первый вопрос: какой толк?
Одна функция с декоратором или класс с двумя методами, есть разница?
В зависимости от задачи я, как использую этот декоратор, так и не ленюсь, пишу класс. Что вообще за вопрос такой. Ну и на всякий случай, мало ли, через `yield` возвращается значение, которое становится доступным в `with`, если определяется переменная после `as`. В `contextmanager` 4 строчки кода, но возможно TC поленился их изучить.
Теперь всё, все покровы сорваны, больше секретов в этой тайной технологии не осталось, ты посвящён в ближний круг :-).

..bw
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB