python, осень 2014: Модули, исключения и менеджеры контекста

46
Лекция 7: Модули, исключения, менеджеры контекста Сергей Лебедев [email protected] 5 ноября 2014 г.

Upload: cs-center

Post on 15-Jun-2015

443 views

Category:

Documents


2 download

DESCRIPTION

Немного про систему импорта и операторы `import` и `from...import`. Исключения, зачем они нужны и как их обрабатывать. Встроенные исключения и базовые классы `BaseException` и `Exception`. Операторы `try...except..else..finally`. Менеджеры контекста и модуль `contextlib`.

TRANSCRIPT

Page 1: Python, осень 2014: Модули, исключения и менеджеры контекста

Лекция 7: Модули, исключения, менеджеры

контекста

Сергей Лебедев

[email protected]

5 ноября 2014 г.

Page 2: Python, осень 2014: Модули, исключения и менеджеры контекста

Модули

Page 3: Python, осень 2014: Модули, исключения и менеджеры контекста

example.py

"""I'm an example.module."""

def f():

return 42

some_variable = "foobar"

1 / 41

Page 4: Python, осень 2014: Модули, исключения и менеджеры контекста

Файл == модуль

• Модулем называется файл с расширением .py.

• Каждый модуль задаёт новое пространство имён, атрибуты

которого соответствуют именам, определённым в файле:

>>> import example

>>> dir(example)

[..., '__cached__', '__doc__', '__file__', '__name__',

'f', 'some_variable']

• Кроме явно определённых имён в модуле также

содержатся:

>>> example.__name__

'example'

>>> example.__doc__

"I'm an example module."

>>> example.__file__

'./example.py'

>>> example.__cached__

'./__pycache__/example.cpython-34.pyc'

2 / 41

Page 5: Python, осень 2014: Модули, исключения и менеджеры контекста

Оператор import

• Оператор import “импортирует” модуль с указанным

именем и создаёт на него ссылку в текущей области

видимости:

>>> import example

>>> example

<module 'example' from './example.py'>

• С помощью оператора as можно явно указать имя

переменной, в которую будет записана ссылка на модуль:

>>> import example as alias

>>> alias

<module 'example' from './example.py'>

• Внутреннее устройство системы импорта в Python 3

выходит за рамки сегодняшней лекции. Интересующиеся

могут обратиться к PEP-3021 и выяснить все детали из

первоисточника.

1http://python.org/dev/peps/pep-03023 / 41

Page 6: Python, осень 2014: Модули, исключения и менеджеры контекста

Подробнее об операторе import

• В момент импорта байт-код модуля выполняется

интерпретатором сверху вниз.

• Полученный в результате импорта модуль попадает в

специальный словарь sys.modules.

• Ключом в словаре является имя модуля, то есть значение

атрибута __name__:

>>> import sys

>>> ("example" in sys.modules, "alias" in sys.modules)

(True, False)

• Повторный импорт уже загруженного модуля не приводит к

его перезагрузке:

>>> import example

>>> id(sys.modules["example"])

4329007128

>>> import example

>>> id(sys.modules["example"])

4329007128

4 / 41

Page 7: Python, осень 2014: Модули, исключения и менеджеры контекста

__name__ == "__main__"

• Чтобы выполнить модуль на Python, достаточно указать путь

к нему в качестве аргумента интерпретатора:

python ./example.py.

• В момент исполнения имя переменной __name__ внутри

модуля example будет иметь специальное значение

"__main__".

• Это можно использовать для того, чтобы добавить в модуль

код, который будет исполняться, только если модуль был

запущен из командной строки, а не импортирован из

другого модуля:

def test():

assert 2 + 2 == 4

if __name__ == "__main__":

print("Running tests.")

test()

5 / 41

Page 8: Python, осень 2014: Модули, исключения и менеджеры контекста

Оператор from...import

• Оператор from...import позволяет импортировать имя из

другого модуля в текущую область видимости:

>>> from example import f

>>> f()

42

• Синтаксис оператора позволяет перечислить несколько

имен через запятую и, возможно, переименовать некоторые

из них:

>>> from example import f as g, some_variable

>>> g()

42

>>> some_variable

'foobar'

6 / 41

Page 9: Python, осень 2014: Модули, исключения и менеджеры контекста

“Семантика” оператора from...import

• Оператор from...import можно однозначно переписать

через оператор import:

>>> from module import foo as bar, boo

# HARDCORE REWRITING MAGIC

>>> import module

>>> bar = module.foo

>>> boo = module.boo

>>> del module # Зачем это нужно?

• Таким образом, всё сказанное про оператор import

релевантно также и для оператора from...import.

7 / 41

Page 10: Python, осень 2014: Модули, исключения и менеджеры контекста

Модули: резюме

• Модуль в Python — это просто файл с расширением .py.

• Модуль можно импортировать целиком или выборочно с

помощью операторов import и from...import.

• Три важных правила импортирования модулей:

• размещайте все импорты в начале модуля,

• сортируйте их в лексикографическом порядке,

• не смешивайте import и from...import.

• Пример:

import os

import sys

from collections import deque

from itertools import groupby

8 / 41

Page 11: Python, осень 2014: Модули, исключения и менеджеры контекста

Исключения

Page 12: Python, осень 2014: Модули, исключения и менеджеры контекста

Зачем нужны исключения?

• Исключения нужны для исключительных ситуаций,например:

• не удалось выделить память для объекта,

>>> [0] * int(1e16)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

MemoryError

• импортируемый модуль не был найден,

>>> import foobar

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ImportError: No module named 'foobar'

• программист написал код, складывающий список и число

>>> [1, 2, 3] + 4

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: can only concatenate list to list

• Исключения — это ошибки, которые можно обрабатывать. В

этом их прелесть.

9 / 41

Page 13: Python, осень 2014: Модули, исключения и менеджеры контекста

Обработка исключений: try...except

• Для обработки исключений в Python используются

операторы try и except:

>>> try:

... something_dangerous()

... except (ValueError, ArithmeticError):

... pass

... except TypeError as e:

... pass

• Ветка except принимает два аргумента:

1. выражение, возвращающее тип или кортеж типов,

2. опциональное имя для перехваченного исключения.

• Исключение e обрабатывается веткой except, если её

первый аргумент expr можно сопоставить с исключением:

isinstance(e, expr)

• При наличии нескольких веток except интерпретатор

сверху вниз ищет подходящую.

10 / 41

Page 14: Python, осень 2014: Модули, исключения и менеджеры контекста

Обработка исключений: подробнее о try...except

• На месте выражения в ветке except может стоять любое

выражение, например, вызов функции или обращение к

переменной:>>> try:

... something_dangerous()

... except Exception as e:

... try:

... something_else()

... except type(e): # Какое исключение мы

... pass # перехватим?

• Время жизни переменной e ограничивается веткой except:>>> try:

... 1 + "42"

... except TypeError as e:

... pass # Что делать, если нам нужно e?

...

>>> e

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'e' is not defined

11 / 41

Page 15: Python, осень 2014: Модули, исключения и менеджеры контекста

Встроенные исключения

• BaseException — базовый класс для встроенных

исключений в Python.

>>> BaseException.__subclasses__()

[<class 'Exception'>, <class 'GeneratorExit'>,

<class 'KeyboardInterrupt'>, <class 'SystemExit'>]

• Напрямую от класса BaseException наследуются только

системные исключения и исключения, приводящие к

завершению работы интерпретатора.

• Все остальные встроенные исключения, а также исключения,

объявленные пользователем, должны наследоваться от

класса Exception.

• Отсюда следует, что, чтобы обработать любое исключение,

достаточно написать:

>>> try:

... something_dangerous()

... except Exception: # Почему не BaseException?

... pass

12 / 41

Page 16: Python, осень 2014: Модули, исключения и менеджеры контекста

Встроенные исключения: AssertionError

• Исключение AssertionError поднимается, когда условие

оператора assert не выполняется:

>>> assert 2 + 2 == 5, ("Math", "still", "works")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AssertionError: ('Math', 'still', 'works')

• Оператор assert используется для ошибок, которые могут

возникнуть только в результате ошибки программиста,

поэтому перехватывать AssertionError считается дурным

тоном.

13 / 41

Page 17: Python, осень 2014: Модули, исключения и менеджеры контекста

Встроенные исключения: ImportError и NameError

• Если оператор import не смог найти модуль с указанным

именем, поднимается исключение ImportError:

>>> import foobar

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ImportError: No module named 'foobar'

• NameError поднимается, если не была найдена локальная

или глобальная переменная:

>>> foobar

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'foobar' is not defined

14 / 41

Page 18: Python, осень 2014: Модули, исключения и менеджеры контекста

Встроенные исключения: AttributeError и LookupError

• Исключение AttributeError поднимается при попытке

прочитать или (в случае __slots__) записать значение в

несуществующий атрибут:

>>> object().foobar

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: 'object' object has no attribute 'foobar'

• Исключения KeyError и IndexError наследуются от

базового класса LookupError и поднимаются, если в

контейнере нет элемента по указанному ключу или индексу:

>>> {}["foobar"]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

KeyError: 'foobar'

>>> [][0]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

IndexError: list index out of range

15 / 41

Page 19: Python, осень 2014: Модули, исключения и менеджеры контекста

Встроенные исключения: ValueError и TypeError

• Исключение ValueError используется в случаях, когда

другие более информативные исключения, например,

KeyError, не применимы:

>>> "foobar".split("")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ValueError: empty separator

• Исключение TypeError поднимается, когда оператор,

функция или метод вызываются с аргументом

несоответствующего типа:

>>> b"foo" + "bar"

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: can't concat bytes to str

• Полный список исключений можно найти в документации

языка2.

2https://docs.python.org/3/library/exceptions.html16 / 41

Page 20: Python, осень 2014: Модули, исключения и менеджеры контекста

Исключения, объявленные пользователем

• Для объявления нового типа исключения достаточно

объявить класс, наследующийся от базового класса

Exception.

• Хорошая практика при написании библиотек на Python —

объявлять свой базовый класс исключений, например:

>>> class CSCException(Exception):

... pass

...

>>> class TestFailure(CSCException):

... def __str__(self):

... return "lecture test failed"

• Наличие базового класса позволяет пользователю

обработать любое исключение, специфичное для

библиотеки в одной ветке except:

>>> try:

... do_something()

... except CSCException:

... # ...

17 / 41

Page 21: Python, осень 2014: Модули, исключения и менеджеры контекста

Интерфейс исключений

Интерфейс исключений в Python довольно нехитрый:

• атрибут args хранит кортеж аргументов, переданных

конструктору исключения,

• атрибут __traceback__ содержит информацию о стеке

вызовов на момент возникновения исключения.

>>> try:

... 1 + "42"

... except Exception as e:

... caught = e

...

>>> caught.args

("unsupported operand type(s) for +: 'int' and 'str'",)

>>> caught.__traceback__

<traceback object at 0x10208d148>

>>> import traceback

>>> traceback.print_tb(caught.__traceback__)

File "<stdin>", line 2, in <module>

18 / 41

Page 22: Python, осень 2014: Модули, исключения и менеджеры контекста

Оператор raise

• Поднять исключение можно с помощью оператора raise:

>>> raise TypeError("type mismatch")

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: type mismatch

• Аргумент оператора raise должен наследоваться от

базового класса BaseException:

>>> raise 42

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: exceptions must derive from BaseException

• Если вызвать оператор raise без аргумента, то он поднимет

последнее пойманное исключение или, если такого

исключения нет, RuntimeError.

>>> raise

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

RuntimeError: No active exception to reraise

19 / 41

Page 23: Python, осень 2014: Модули, исключения и менеджеры контекста

Обработка исключений: try...finally

• Иногда требуется выполнить какое-то действие вне

зависимости от того, произошло исключение или нет,

например, закрыть файл:

>>> try:

... handle = open("example.txt", "wt")

... try:

... do_something(handle)

... finally:

... handle.close()

... except IOError as e:

... print(e, file=sys.stderr)

• Аналогичным образом нужно работать с любыми другими

ресурсами: сетевыми соединениями, примитивами

синхронизации.

20 / 41

Page 24: Python, осень 2014: Модули, исключения и менеджеры контекста

Обработка исключений: try...else

• С помощью ветки else можно выполнить какое-то действие

в ситуации, когда внутри try блока не возникло

исключения:

>>> try:

... handle = open("example.txt", "wt")

... else:

... report_success(handle)

... except IOError as e:

... print(e, file=sys.stderr)

• Чем использование else лучше следующего варианта?

>>> try:

... handle = open("example.txt", "wt")

... report_success(handle)

... except IOError as e:

... print(e, file=sys.stderr)

21 / 41

Page 25: Python, осень 2014: Модули, исключения и менеджеры контекста

Исключения: резюме

• Механизм обработки исключений в Python похож на

аналогичные конструкции в С++ и Java, но Python расширяет

привычную пару try...except с помощью веток else и

finally.

• Поднять исключение можно с помощью оператора raise,

его семантика эквивалентна throw в C++ и Java.

• В Python много встроенных типов исключений, которые

можно и нужно использовать при написании функций и

методов.

• Для объявления нового типа исключения достаточно

унаследоваться от базового класса Exception.

• Два важных правила при работе с исключениями:

• минимизируйте размер ветки try,

• всегда старайтесь использовать наиболее специфичный тип

исключения в ветке except.

22 / 41

Page 26: Python, осень 2014: Модули, исключения и менеджеры контекста

Менеджерыконтекста

Page 27: Python, осень 2014: Модули, исключения и менеджеры контекста

Зачем нужны менеджеры контекста?

• Менеджеры контекста позволяют компактно выразить уже

знакомый нам паттерн управления ресурсами:

>>> r = acquire_resource()

... try:

... do_something(r)

... finally:

... release_resource(r)

• С помощью менеджера контекста пример выше можно

записать так:

>>> with acquire_resource() as r:

... do_something(r)

Действие release_resource будет выполнено

автоматически, вызывать его явно не нужно.

23 / 41

Page 28: Python, осень 2014: Модули, исключения и менеджеры контекста

Протокол менеджеров контекста

• Протокол менеджеров контекста состоит из двух методов.

• Метод __enter__ инициализирует контекст, например,

открывает файл или захватывает мьютекс. Значение,

возвращаемое методом __enter__, записывается по имени,

указанному после оператора as.• Метод __exit__ вызывается после выполнения телаоператора with. Метод принимает три аргумента:

1. тип исключения,

2. само исключение и

3. объект типа traceback.

Если в процессе исполнения тела оператора with было

поднятно исключение, метод __exit__ может подавить его,

вернув True.

• Экземпляр любого класса, реализующего эти два метода,

является менеджером контекста.

24 / 41

Page 29: Python, осень 2014: Модули, исключения и менеджеры контекста

Duck Typing3

3http://theregister.co.uk/Print/2007/05/06/fables25 / 41

Page 30: Python, осень 2014: Модули, исключения и менеджеры контекста

“Семантика” оператора with

• Напоминание:

>>> with acquire_resource() as r:

... do_something(r)

• Процесс исполнение оператора with можно концептуально

записать так:

>>> manager = acquire_resource()

>>> r = manager.__enter__()

>>> try:

... do_something(r)

... finally:

... exc_type, exc_value, tb = sys.exc_info()

... suppress = manager.__exit__(exc_type,

... exc_value, tb)

... if exc_value is not None and not suppress:

... raise exc_value

26 / 41

Page 31: Python, осень 2014: Модули, исключения и менеджеры контекста

Расширенные возможности оператора with

• Оператор with позволяет работать с несколькими

контекстными менеджерами одновременно:

>>> with acquire_resource() as r, \

... acquire_other_resource() as other:

... do_something(r, other)

Такая запись эквивалентна двум вложенным менеджерам

контекста:

>>> with acquire_resource() as r:

... with acquire_other_resource() as other:

... do_something(r, other)

• Можно также использовать оператор with без указания

имени переменной:

>>> with acquire_resource():

... do_something()

27 / 41

Page 32: Python, осень 2014: Модули, исключения и менеджеры контекста

Примеры менеджеров контекста: opened

>>> from functools import partial

>>> class opened:

... def __init__(self, path, *args, **kwargs):

... self.opener = partial(open, path,

... *args, **kwargs)

...

... def __enter__(self):

... self.handle = self.opener()

... return self.handle

...

... def __exit__(self, *exc_info):

... self.handle.close() # Почему можно обойтись

... del self.handle # без return?

...

>>> with opened("./example.txt", mode="rt") as handle:

... pass

Капитан сообщает

opened интересен только в качестве примера, потому что файлы

в Python уже поддерживают протокол менеджеров контекста.

28 / 41

Page 33: Python, осень 2014: Модули, исключения и менеджеры контекста

Примеры менеджеров контекста: модуль tempfile

• Модуль tempfile реализует классы для работы с

временными файлами.

• Все классы реализуют протокол менеджеров контекста,

которые работают так же, как и для обычных файлов.

• Интересный пример — класс NamedTemporaryFile, который

автоматически удаляет временный файл при выходе из

менеджера контекста:

>>> import tempfile

>>> with tempfile.NamedTemporaryFile() as handle:

... path = handle.name

... print(path)

...

/var/folders/nj/T/tmptpy6nn5y

>>> open(path)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

FileNotFoundError: [Errno 2] No such file or directory

29 / 41

Page 34: Python, осень 2014: Модули, исключения и менеджеры контекста

Примеры менеджеров контекста: syncronized

>>> class synchronized:

... def __init__(self):

... self.lock = threading.Lock()

...

... def __enter__(self):

... self.lock.acquire()

...

... def __exit__(self, *exc_info):

... self.lock.release()

...

>>> with synchronized():

... do_something()

Капитан сообщает

Большая часть примитивов синхронизации в Python, включая

класс Lock, реализует протокол менеджера контекста.

Использовать менеджер synchronized не нужно — он интересен

только в качестве примера.

30 / 41

Page 35: Python, осень 2014: Модули, исключения и менеджеры контекста

Примеры контекстных менеджеров: cd

>>> import os

>>> class cd:

... def __init__(self, path):

... self.path = path

...

... def __enter__(self):

... self.saved_cwd = os.getcwd()

... os.chdir(self.path)

...

... def __exit__(self, *exc_info):

... os.chdir(self.saved_cwd)

...

>>> print(os.getcwd())

./csc/python

>>> with cd("/tmp"):

... print(os.getcwd())

...

/tmp

31 / 41

Page 36: Python, осень 2014: Модули, исключения и менеджеры контекста

Менеджеры контекста: резюме

• Менеджеры контекста — удобный способ управлять

жизненным циклом ресурсов в Python.

• Для работы с менеджером контекста используется оператор

with.

• Менеджером контекста является экземпляр любого класса,

реализующего методы __enter__ и __exit__.

• Некоторые встроенные типы, например, файлы и

примитивы синхронизации уже поддерживают протокол

менеджеров контекста — этим можно и нужно пользоваться

при написании кода.

32 / 41

Page 37: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль

contextlib

Page 38: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: closing

• Менеджер контекста closing обобщает логику уже

известного нам opened на экземпляр любого класса,

реализующего метод close.

• Реализовать closing самому несложно, но приятно, когда в

стандартной библиотеке языка есть и такие мелочи.

• С помощью closing можно, например, безопасно работать с

HTTP ресурсами:

>>> from contextlib import closing

>>> from urllib.request import urlopen

>>> url = "http://compscicenter.ru"

>>> with closing(urlopen(url)) as page:

... do_something(page)

33 / 41

Page 39: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: redirect_stdout

• Менеджер контекста redirect_stdout позволяет локально

перехватывать вывод в стандартный поток.

• Пример использования:

>>> from contextlib import redirect_stdout

>>> import io

>>> handle = io.StringIO()

>>> with redirect_stdout(handle):

... print("Hello, World!")

...

>>> handle.getvalue()

'Hello, World!\n'

Вопрос

Как можно было бы реализовать redirect_stdout?

34 / 41

Page 40: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: suppress

• С помощью менеджера контекста suppress можно

локального подавить исключения указанных типов:>>> from contextlib import suppress

>>> with suppress(FileNotFoundError):

... os.remove("example.txt")

• Реализация менеджера не хитра:>>> class supress:

... def __init__(self, *suppressed):

... self.suppressed = suppressed

...

... def __enter__(self):

... pass

...

... def __exit__(self, exc_type, exc_value, tb):

... return (exc_type is not None and

... issubclass(exc_type, suppressed))

• При использовании suppress, как и в целом при работе с

исключениями, стоит указывать ниболее специфичный тип

исключения.35 / 41

Page 41: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: ContextDecorator

• Базовый класс ContextDecorator позволяет объявлять

менеджеры контекста, которые можно использовать как

декораторы.

• Зачем это нужно?

def f():

with context():

# ...

@context()

def f():

# ...

• Переход к синтаксису декораторов:

• подчеркивает, что менеджер контекста применяется ко

всему телу функции,

• позволяет сэкономить 4 пробела :)

Вопрос

Как должен быть реализован менеджер контекста, чтобы его

можно было использовать в качестве декоратора?

36 / 41

Page 42: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: менеджеры контекста и декораторы

• Для того, чтобы менеджер контекста можно было

использовать как декоратор, достаточно унаследовать его

от ContextDecorator.

• Модифицируем менеджер suppress из модуля contextlib,

чтобы с помощью него можно было подавлять исключения

во всей функции:

>>> from contextlib import (suppress as _suppress,

... ContextDecorator)

>>> class suppressed(_suppress, ContextDecorator):

... pass

...

>>> @suppressed(IOError)

... def do_something():

... pass

37 / 41

Page 43: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: ExitStack

• Что делать, если количество ресурсов может быть

произвольным? Например:

>>> def merge_logs(output_path, *logs):

... handles = open_files(logs)

... with open(output_path, "wt") as output:

... merge(output, handles)

... close_files(logs)

• Правильный ответ: ExitStack. Менеджер ExitStack

позволяет управлять произвольным количеством

менеджеров контекста:

>>> from contextlib import ExitStack

>>> def merge_logs(output_path, *logs):

... with ExitStack() as stack:

... handles = [stack.enter_context(open(log))

... for log in logs]

... output = open(output_path, "wt")

... stack.enter_context(output)

... merge(output, handles)

38 / 41

Page 44: Python, осень 2014: Модули, исключения и менеджеры контекста

“Семантика” менеджера ExitStack

• Менеджер ExitStack поддерживает стек вложенных

менеджеров контекста:

>>> with ExitStack() as stack:

... stack.enter_context(some_resource)

... stack.enter_context(other_resource)

... do_something(some_resource, other_resource)

• При выходе из контекста, ExitStack обходит список

вложенных менеджеров контекста в обратном порядке и

вызывает у каждого менеджера метод __exit__.

• Менеджер ExitStack корректно обрабатывает ситуации,

• когда метод __exit__ подавил исключение

• или, когда в процессе работы метода __exit__ возникло

новое исключение.

39 / 41

Page 45: Python, осень 2014: Модули, исключения и менеджеры контекста

Менеджер ExitStack и вызываемые функции

Полезная особенность менеджера ExitStack — возможность

зарегистрировать произвольную функцию на выполнение в

момент выхода из контекста:

>>> with ExitStack() as stack:

... @stack.callback

... def cleanup():

... print("Cleaning up.")

...

... def more_cleanup(arg):

... print("More cleaning up: {}.".format(arg))

... stack.callback(more_cleanup,

... "important resource")

...

More cleaning up: important resource.

Cleaning up. # LIFO again.

Вопрос

Для чего это может быть полезно?

40 / 41

Page 46: Python, осень 2014: Модули, исключения и менеджеры контекста

Модуль contextlib: резюме

• Модуль contextlib содержит функции и классы,

украшающие жизнь любителя менеджеров контекста.

• Мы поговорили про:

• closing,

• redirect_stdout,

• suppress,

• ContextDecorator,

• ExitStack.

41 / 41