Тип contextmanager, контекстный менеджер
Оператор with в Python поддерживает концепцию контекста среды выполнения, определенного контекстным менеджером. Типичные области применения контекстных менеджеров включают сохранение и восстановление различных типов глобального состояния, блокировку и разблокировку ресурсов, закрытие открытых файлов и т. д.
Содержание:
- Оператор контекста with ;
- Как работает менеджер контекста;
- Метод __enter__() ;
- Метод __exit__() ;
Синтаксис оператора контекста with :
with EXPRESSION as TARGET: SUITE
manager = (EXPRESSION) enter = type(manager).__enter__ exit = type(manager).__exit__ value = enter(manager) hit_except = False try: TARGET = value SUITE except: hit_except = True if not exit(manager, *sys.exc_info()): raise finally: if not hit_except: exit(manager, None, None, None)
Выражение EXPRESSION , непосредственно следующее за ключевым словом with является «выражением контекста», так как это выражение обеспечивает основной ключ к среде выполнения, которую менеджер контекста устанавливает для продолжительности тела выражения.
Как работает менеджер контекста with :
- Выражение контекста (выражение, указанное в EXPRESSION ) оценивается для получения менеджера контекста.
- Менеджер контекста загружает метод __enter__() для последующего использования.
- Менеджер контекста загружает метод __exit__() для последующего использования.
- Менеджер контекста вызывает метод __enter__() .
- Если TARGET была включена в оператор with , то ей присваивается возвращаемое значение из метода __enter__() .
Обратите внимание, что оператор with гарантирует, что если метод __enter__() возвращается без ошибки, то всегда будет вызываться метод __exit__() . Таким образом, если ошибка возникает во время присваивания значения через оператор as , то она будет обрабатываться так же, как и ошибка, возникающая внутри with . - Последовательность команд выполнена.
- Вызван метод __exit__() . Если исключение вызвало выход из последовательности команд, то его тип exc_type , значение exc_val и информация о трассировке exc_tb передаются в качестве аргументов __exit__() . В противном случае предоставляется три аргумента None .
Если последовательность команд была завершена из-за исключения, а возвращаемое значение из метода __exit__() было False , то исключение вызывается повторно. Если возвращаемое значение было True , то исключение подавляется и выполнение продолжается с оператора, следующего за оператором with .
Если последовательность команд была завершена по любой причине, кроме исключения, то возвращаемое значение из __exit__() игнорируется, и выполнение продолжается.
При наличии нескольких контекстных менеджеров, то они обрабатываются так, как если бы несколько операторов with были вложенными:
with A() as a, B() as b: SUITE # Эквивалентно with A() as a: with B() as b: SUITE
С версии Python 3.10 поддерживается использование круглых скобок для написания нескольких диспетчеров. Это позволяет форматировать длинную коллекцию диспетчеров контекста в несколько строк аналогично тому, как это можно с операторами импорта. Например, теперь действительны все эти примеры:
with (CtxManager() as example): . with ( CtxManager1(), CtxManager2() ): . with (CtxManager1() as example, CtxManager2()): . with (CtxManager1(), CtxManager2() as example): . with ( CtxManager1() as example1, CtxManager2() as example2 ): .
Допускается использовать конечную запятую в конце заключенной группы:
with ( CtxManager1() as example1, CtxManager2() as example2, CtxManager3() as example3, ): .
Реализация/протокол менеджера контекста.
Протокол контекстных менеджеров реализован с помощью пары методов, которые позволяют определяемым пользователем классам определять контекст среды выполнения, который вводится до выполнения тела инструкции и завершается при завершении инструкции:
contextmanager.__enter__() :
Метод contextmanager.__enter__() вводит контекст среды выполнения и возвращает либо себя, либо другой объект, связанный с контекстом среды выполнения. Значение, возвращаемое этим методом, привязывается к идентификатору в предложении as оператора with , использующего этот контекстный менеджер.
Ярким примером контекстного менеджера, который возвращает себя, является объект file . Файловые объекты возвращают себя из __enter__() , чтобы разрешить использование встроенной функции open() в качестве контекстного выражения в операторе with .
with open('/etc/passwd') as fp: for line in fp: print line.rstrip()
contextmanager.__exit__(exc_type, exc_val, exc_tb) :
Метод contextmanager.__exit__() предоставляет выход из контекста среды выполнения и возвращает логический флаг, указывающий, следует ли подавлять любое возникшее исключение. При возникновении исключения во время выполнения тела оператора with , аргументы содержат тип исключения exc_type , значение exc_val и информацию о трассировке exc_tb . В противном случае все три аргумента — это None .
Если у метода contextmanager.__exit__() установить возвращаемое значение в return True , то это приведет к тому, что оператор with будет подавлять возникающие исключения внутри себя и продолжит выполнение с оператора, непосредственно следующим за оператором with . В противном случае исключение exc_type продолжает распространяться после завершения выполнения этого метода. Исключения, возникающие во время выполнения этого метода, заменят все исключения, возникшие в теле оператора with .
Передаваемое исключение exc_type никогда не следует повторно вызывать явно, вместо этого метод contextmanager.__exit__() должен возвращать return False , чтобы указать, что метод завершился успешно и не хочет подавлять возникшее исключение. Это позволяет коду управления контекстом легко определять, действительно ли метод contextmanager.__exit__() потерпел неудачу.
Упрощенное создание менеджеров контекста.
Поддержка упрощенного создания менеджеров контекста предоставляется модулем contextlib .
Многие контекстные менеджеры, например, файлы и контексты на основе генераторов будут одноразовыми объектами. После вызова метода __exit__() менеджер контекста больше не будет находиться в работоспособном состоянии (например, файл был закрыт или базовый генератор завершил выполнение).
Необходимость создания нового объекта менеджера контекста для каждого оператора with — это самый простой способ избежать проблем с многопоточным кодом и вложенными операторами, пытающимися использовать один и тот же контекстный менеджер. Не случайно, что все стандартные менеджеры контекста модуля contextlib , поддерживающие повторное использование, происходят из модуля threading и все они уже разработаны для решения проблем, создаваемых потоковым и вложенным использованием.
Это означает, что для сохранения менеджера контекста с определенными аргументами инициализации, которые будут использоваться в нескольких операторах with , как правило, необходимо будет сохранить его в вызываемом объекте с нулевым аргументом, который затем вызывается в выражении контекста каждого оператора, а не кэшировать непосредственно менеджер контекста. Если это ограничение не применяется, это должно быть ясно указано в документации соответствующего контекстного менеджера.
Примеры использования менеджеров контекста:
Шаблон для обеспечения того, что блокировка, полученная в начале блока, освобождается, когда блок закончен:
@contextmanager def locked(lock): lock.acquire() try: yield finally: lock.release()
with locked(myLock): # Здесь код выполняется с удержанным myLock. # lock.release() гарантированно будет выполнен, когда блок # будет завершен (даже по необработанному исключению).
Шаблон для открытия файла, который обеспечивает закрытие файла при завершении блока:
@contextmanager def opened(filename, mode="r"): f = open(filename, mode) try: yield f finally: f.close()
with opened("/etc/passwd") as f: for line in f: print line.rstrip()
Шаблон для фиксации или отката транзакции базы данных:
@contextmanager def transaction(db): db.begin() try: yield None except: db.rollback() raise else: db.commit()
Временно перенаправить стандартный вывод для однопоточных программ:
@contextmanager def stdout_redirected(new_stdout): save_stdout = sys.stdout sys.stdout = new_stdout try: yield None finally: sys.stdout = save_stdout
with opened(filename, "w") as f: with stdout_redirected(f): print "Hello world"
Вариант с функцией open() , который также возвращает условие ошибки:
@contextmanager def opened_w_error(filename, mode="r"): try: f = open(filename, mode) except IOError, err: yield None, err else: try: yield f, None finally: f.close()
with opened_w_error("/etc/passwd", "a") as (f, err): if err: print "IOError:", err else: f.write("guido::0:0::/:/bin/sh\n")
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Утиная типизация ‘Duck Typing’
- Что такое вызываемый объект callable?
- Как проверить тип переменной/объекта
- Логический тип данных bool
- Целые числа int
- Ограничение длины преобразования целочисленной строки
- Вещественные числа float
- Комплексные числа complex
- Типы последовательностей
- Список list
- Кортеж tuple
- Диапазон range
- Текстовые строки str
- Словарь dict
- Множество set и frozenset
- Итератор Iterator, протокол итератора
- Генератор generator и выражение yield
- Контекстный менеджер with
- Байтовые строки bytes
- Байтовый массив bytearray
- Тип memoryview, буфер обмена
- Файловый объект file object
- Универсальный псевдоним GenericAlias
- Объект объединения Union
Контекстные менеджеры в Python
В программах часто используются такие ресурсы, как файлы или базы данных. Но эти ресурсы ограничены в доступе. Поэтому основная проблема заключается в том, чтобы освободить их после использования. Если они не будут освобождены, это приведет к утечке ресурсов и может вызвать замедление работы системы или ее сбой. Было бы хорошо, если бы у пользователя был механизм для автоматического захвата и освобождения ресурсов. В Python это может быть достигнуто с помощью контекстных менеджеров, которые облегчают правильную обработку ресурсов.
Для работы с файлами часто используется конструкция, показанная в примере ниже. Она позволяет захватить ресурс, выполнить необходимые операции, а затем освободить его:
with open("test.txt") as f: data = f.read()
Давайте возьмем пример управления файлами. Когда файл открывается, используется дескриптор файла, который является ограниченным ресурсом. Только определенное количество файлов может быть открыто процессом одновременно. Следующая программа демонстрирует это.
file_descriptors = [] for x in range(100000): file_descriptors.append(open('test.txt', 'w'))
Traceback (most recent call last):
File "context.py", line 3, in
OSError: [Errno 24] Too many open files: 'test.txt'Мы получили сообщение об ошибке, указывающее, что открыто слишком много файлов. Приведенный выше пример представляет собой случай утечки файлового дескриптора. Это происходит потому, что слишком много открытых файлов и они не закрыты. Бывает, что программист просто забывает закрыть открытый файл.
Управление ресурсами с помощью контекстного менеджера
Предположим, что блок кода вызывает исключение или содержит сложный алгоритм с несколькими инструкциями return , и тогда становится неудобно закрывать файл во всех местах.
Как правило, в других языках при работе с файлами для того, чтобы файловый ресурс был закрыт после использования, даже если было возбуждено исключение, используется конструкция try-except-finally. Python предоставляет простой способ управления ресурсами: контекстные менеджеры. Выражение, следующее после ключевого слова with , должно возвращать объект, реализующий протокол Context Manager. Контекстные менеджеры могут быть написаны с использованием классов или функций (с помощью декораторов).
Создание контекстного менеджера
При создании контекстных менеджеров с использованием классов пользователь должен убедиться, что у класса есть следующие методы: __enter__() и __exit__() . __enter__() возвращает требуемый ресурс, а __exit __() ничего не возвращает, но выполняет освобождение ресурсов.
Для начала, давайте создадим простой класс ContextManager , чтобы понять базовую структуру создания контекстных менеджеров с использованием классов, как показано ниже:
class ContextManager(): def __init__(self): print('init method called') def __enter__(self): print('enter method called') return self def __exit__(self, exc_type, exc_value, exc_traceback): print('exit method called') with ContextManager() as manager: print('with statement block')
init method called
enter method called
with statement block
exit method calledВ этом случае создается объект ContextManager . Он присваивается переменной, следующей за ключевым словом as ( manager в данном случае). При запуске вышеуказанной программы последовательно выполняются следующие действия:
- __init__()
- __enter__()
- инструкции (код внутри блока with )
- __exit__() [параметры этого метода используются для управления исключениями]
Управление файлами с помощью контекстного менеджера
Давайте используем вышеприведенную концепцию для создания класса, который применяется для управлении файловыми ресурсами. Класс FileManager помогает открыть файл, записать/прочитать содержимое, а затем закрыть его.
class FileManager(): def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_value, exc_traceback): self.file.close() # загрузка файла with FileManager('test.txt', 'w') as f: f.write('Test') print(f.closed)
True
Управление файлами с помощью контекстного менеджера и конструкции with
При выполнении блока with последовательно выполняются следующие операции:
- Создается объект FileManager с аргументами test.txt и w (write) при выполнении метода __init__ .
- Метод __enter__ открывает файл test.txt в режиме записи (write) и возвращает объект FileManager , который присваивается переменной f .
- Текст «Test» записывается в файл.
- Метод __exit__ обеспечивает закрытие файла при выходе из блока with .
Когда выполняется print(f.closed) , то на экран выводится True , поскольку FileManager уже позаботился о закрытии файла, что в противном случае нужно было бы сделать явно.
Управление подключением к базе данных с помощью контекстного менеджера
Давайте создадим простую систему управления подключениями к базе данных. Количество подключений к базе данных, которые могут быть открыты одновременно, также ограничено (как и файловые дескрипторы). Поэтому контекстные менеджеры полезны при управлении соединениями с базой данных, так как есть вероятность, что программист может забыть закрыть соединение.
from pymongo import MongoClient class MongoDBConnectionManager(): def __init__(self, hostname, port): self.hostname = hostname self.port = port self.connection = None def __enter__(self): self.connection = MongoClient(self.hostname, self.port) return self def __exit__(self, exc_type, exc_value, exc_traceback): self.connection.close() # подключение к localhost with MongoDBConnectionManager('localhost', '27017') as mongo: collection = mongo.connection.SampleDb.test data = collection.find() print(data.get('name'))
Управление подключениями к базе данных с помощью контекстного менеджера и оператора with
При выполнении блока with последовательно выполняются следующие операции:
- Создается объект MongoDBConnectionManager с аргументами localhost и 27017 при выполнении метода __ init__ .
- Метод __enter__ открывает соединение mongodb и возвращает объект MongoDBConnectionManager переменной mongo .
- Осуществляется доступ к коллекции (collection) test в базе данных SampleDb и извлекается документ с _id=1 . На экран выводится поле name документа.
- Метод __exit__ обеспечивает закрытие соединения при выходе из блока with .
Как устроен with
Контекстные менеджеры (оператор with ) встречаются там, где перед совершением действия нужно что-то настроить, а после – прибраться. Например, чтобы прочитать файл, мы используем такой контекстный менеджер:
with open(filepath) as file_handler: return json.load(file_handler)
Здесь “настройкой” является открытие файла, а “уборкой” – его закрытие. Сейчас мы разберемся, как утроены контекстные менеджеры в Питоне.
Свой контекстный менеджер
Заменим нетипичную конструкцию with. as более распространенной try. finally :
file_handler = open(filepath).__enter__() try: print(json.load(file_handler)) finally: file_handler.__exit__()
Мы видим, что файл открывается при вызове __enter__ , а закрывается при вызове __exit__ . При этом закрытие файла произойдет, даже если json.load бросит исключение.
Напишем собственный контекстный менеджер, который создаст книгу в Excel, а по завершении работы с ней – сохранит. Другими словами, мы хотели бы использовать его вот так:
import openpyxl with create_workbook('workbook_name.xlsx') as workbook: fill_worksheet(workbook.active, some_data)
Согласно документации, для этого нам достаточно написать класс, который определяет методы __enter__ и __exit__ . Вот так он будет выглядеть:
class CreateWorkbook: def __init__(self, filepath): self.filepath = filepath def __enter__(self): self.workbook = Workbook() return self.workbook def __exit__(self, *args): self.workbook.save(self.filepath)
Соответственно, использовать его можно так:
with CreateWorkbook('workbook_name.xlsx') as workbook: worksheet = workbook.active worksheet.cell(row=1, column=1, value="1")
И этот код действительно создает книгу и записывает число 1 в первую ячейку. Проблема только в том, что так контекстные менеджеры не пишут.
Свой контекстный менеджер, ver. 2.0
Первый недостаток нашего контекстного менеджера заключается в том, что он позволяет вызвать __exit__ перед __enter__ . Если это произойдет, поднимется непонятное исключение. Для того, чтобы избежать этого, воспользуемся генератором:
from openpyxl import Workbook def create_workbook(filepath): workbook = Workbook() yield workbook workbook.save(filepath) class CreateWorkbook: def __init__(self, filepath): self.filepath = filepath def __enter__(self): self.generator = create_workbook(self.filepath) return next(self.generator) def __exit__(self, *args): next(self.generator, None)
В вызове next(self.generator, None) второй аргумент нужен для того, чтобы next не поднимал исключение StopIteration .
Теперь заметим, что генератор create_workbook содержит всю предметно-ориентированную логику. Поэтому класс CreateWorkbook можно обобщить так, чтобы он от нее не зависил и работал с любыми генераторами. Заодно переименуем его в ContextManager :
class ContextManager: def __init__(self, generator): self.generator = generator def __call__(self, *args, **kwargs): self.args, self.kwargs = args, kwargs return self def __enter__(self): self.generator_instance = self.generator(*self.args, **self.kwargs) return next(self.generator_instance) def __exit__(self, *args): next(self.generator_instance, None)
В конструкторе ContextManager мы принимаем генератор, при вызове объекта записываем переданные генератору параметры, а при вызове __enter__ вызываем генератор. Теперь класс ContextManager можно использовать следующим образом:
from openpyxl import Workbook def create_workbook(filepath): workbook = Workbook() yield workbook workbook.save(filepath) with ContextManager(create_workbook)('workbook_name.xlsx') as workbook: worksheet = workbook.active worksheet.cell(row=1, column=1, value="1")
Вызов ContextManager(create_workbook)(‘workbook_name.xlsx’) выглядит некрасиво. Исправим это:
create_workbook = ContextManager(create_workbook) with create_workbook('workbook_name.xlsx') as workbook: worksheet = workbook.active worksheet.cell(row=1, column=1, value="1")
Здесь заметим, что ContextManager(create_workbook) , по сути, возвращает тот же объект create_workbook , но с дополнительным поведением. Ровно для этой задачи в Питоне существует специальный синтаксический сахар – декораторы. Нам даже не потребуется изменять ContextManager :
@ContextManager def create_workbook(filepath): workbook = Workbook() yield workbook workbook.save(filepath) with create_workbook('workbook_name.xlsx') as workbook: worksheet = workbook.active worksheet.cell(row=1, column=1, value="1")
Самое приятное, что наш универсальный ContextManager уже есть в стандартной библиотеке Питона, и называется он contextmanager . Весь наш пример можно переписать так:
from contextlib import contextmanager from openpyxl import Workbook @contextmanager def create_workbook(filepath): workbook = Workbook() yield workbook workbook.save(filepath) with create_workbook('workbook_name.xlsx') as workbook: worksheet = workbook.active worksheet.cell(row=1, column=1, value="1")
И это еще не всё. Если после yield workbook пользовательский код поднимит исключение, мы не сохраним книгу. Чтобы этого избежать, обернем это выражение в try. finally :
@contextmanager def create_workbook(filepath): workbook = Workbook() try: yield workbook finally: workbook.save(filepath)
А вот так контекстные менеджеры писать принято.
Резюме
- Контекстные менеджеры нужны там, где есть какая-то “настройка”, действия пользователя и следующая за ними “уборка”.
- У контекстного менеджера обязательно есть атрибуты __enter__ и __exit__ .
- Не обязательно писать целый класс для нового контекстного менеджера, достаточно обернуть генератор в декоратор contextmanager .
- yield стоит оборачивать в блок try. finally .
Дальнейшее чтение
- Более подробное описание методов __enter__ и __exit__ .
- Подброрка библиотечных контекстных менеджеров и рецептов к ним.
- Часть доклада Рэймонда Хэттингера, одного из разработчиков Питона, про декораторы и контекстные менеджеры.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Python. Урок 21. Работа с контекстным менеджером
Контекстные менеджеры позволяют задать поведение при работе с конструкцией with: при входе и выходе из блока. Это упрощает работу с ресурсами в части их захвата и освобождения; транзакциями, когда нужно либо полностью закончить транзакцию, либо откатить ее целиком. Этой теме будет посвящен данный урок.
- Работа с контекстным менеджером
- Создание своего контекстного менеджера
- Работа с contextlib
Работа с контекстным менеджером
Рассмотрим пример, на котором будут показаны преимущества работы с контекстным менеджером. Задача состоит в следующем: записать в файл file.txt строку hello .
Самый простой способ ее решить – это воспользоваться функцией open() для открытия файла и записать в него данные через функцию write() . При этом нужно не забыть освободить ресурс, вызвав функцию close() .
f = open(‘file.txt’, ‘w’) f.write(‘hello’) f.close()
Но это не очень хорошее решение, если в процессе работы с файлом (запись, чтение), произошло исключение, то функция close() не будет вызвана, что влечет за собой возможную потерю данных. Для решения этого вопроса воспользуемся обработкой исключения:
f = open('file.txt', 'w') try: f.write('hello') except: print('Some error!') finally: f.close()
Для того, чтобы не писать дополнительный код, связанный с обработкой исключений (это неудобно и об этом можно забыть), можно воспользоваться конструкцией with… as :
with open('file.txt', 'w') as f: f.write('hello')
Такая конструкция позволяет захватить ресурс (в данном случае файл), выполнить нужный набор операций (запись данных), а перед выходом – освободить ресурс.
Создание своего контекстного менеджера
Если сущность, которую вы создаете, по стилю работы с ней похожа на файл, т.е. предполагает захват ресурса и освобождение, либо требует выполнения определенных действий перед началом работы и при завершении, то хорошим решением будет создать свой контекстный менеджер, с которым можно будет работать с помощью конструкции with..as. Для этого, в класс необходимо добавить два метода: __enter__ и __exit__ .
Перед тем как перейти к примеру, демонстрирующему работу с этими функциями, рассмотрим, что происходит (какие методы и в каком порядке вызываются) в конструкции:
with open('file.txt', 'w') as file_data: file_data.write('hello')
- Оператор with сохраняет метод __exit__ класса File .
- Вызывается метод __enter__ класса File .
- __enter__ открывает файл и возвращает его.
- Дескриптор открытого класса передается в file_data .
- В файл записываются данные через метод write.
- Вызывается сохраненный метод __exit__ , который закрывает файл.
Если внутри конструкции with происходит исключение, то оно передается в метод __exit__ , в котором производится его обработка и освобождение ресурсов (закрытие файла).
Пример реализации контекстного менеджера
Создадим класс, у объекта которого необходимо вызывать метод post_work() перед прекращением работы с ним:
class Resource: def __init__(self, name): print('Resource: create <>'.format(name)) self.__name = name def get_name(self): return self.__name def post_work(self): print('Resource: close')
Теперь создадим контекстный менеджер для работы с Resource , который можно будет использовать с оператором with:
class ResourceForWith: def __init__(self, name): self.__resource = Resource(name) def __enter__(self): return self.__resource def __exit__(self, type, value, traceback): self.__resource.post_work()
Пример работы с ResourceForWith и конструкцией with :
with ResourceForWith('Worker') as r: print(r.get_name())
Если выполнить этот код, то получим следующий вывод на консоль
>python test.py Resource: create Worker Worker Resource: close
Работа с contextlib
В стандартную библиотеку Python входит модуль contextlib , который содержит утилиты для построения и работы с контекстными менеджерами.
Рассмотрим только один инструмент из всего набора – contextmanager . contextmanager используется как декоратор для функции, превращая ее в контекстный менеджер. При этом схема конструирования такая: все, что написано до оператора yield вызывается в рамках функции __enter__ , а все что после – в рамках __exit__ .
Рассмотрим несколько примеров:
from contextlib import contextmanager @contextmanager def processor(): print('--> start processing') yield print('
В contextmanager можно завернуть работу с файлом :
from contextlib import contextmanager @contextmanager def open_file(path, mode): f = open(path, mode) yield f f.close() with open_file('test.txt', 'w') as fw: fw.write('hello')
P.S.
Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта . Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Раздел: Python Уроки по Python Метки: Python, Уроки Python
Python. Урок 21. Работа с контекстным менеджером : 4 комментария
- Олег 02.04.2020 Добрый день! Подскажите, как можно реализовать менеджер контекста для измерения времени выполнения какого-либо кода с помощью with и показывать результат измерения после завершения исполнения этого кода.
по типу: >>> with Timer ():
do_some_long_stuff () Time: 10.004614966004738
- yossik 11.04.2020 Например вот так: from contextlib import contextmanager
import time @contextmanager
def timer():
start_time = time.time()
yield
print(“— %s seconds —” % (time.time() – start_time)) with timer():
for i in range(1000):
junk = list(range(i**2))
print(“–DONE–“)