Введение в контекстные менеджеры и оператор with
Менеджер контекста является объектом , который получает уведомление , когда контекст (блок кода) начинается и заканчивается. Вы обычно используете один с with
заявлением. Он заботится об уведомлении.
Например, файловые объекты являются контекстными менеджерами. Когда контекст заканчивается, объект файла закрывается автоматически:
open_file = open(filename)
with open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
В приведенном выше примере, как правило , упрощена, используя в as
ключевого слова:
with open(filename) as open_file:
file_contents = open_file.read()
# the open_file object has automatically been closed.
Все, что завершает выполнение блока, вызывает метод exit менеджера контекста. Это включает исключения и может быть полезно, когда ошибка приводит к преждевременному выходу из открытого файла или соединения. Выход из сценария без надлежащего закрытия файлов / соединений - плохая идея, которая может привести к потере данных или другим проблемам. С помощью диспетчера контекста вы можете всегда принимать меры предосторожности, чтобы предотвратить повреждение или потерю таким образом. Эта функция была добавлена в Python 2.5.
Присвоение цели
Многие контекстные менеджеры возвращают объект при вводе. Вы можете назначить этот объект на новое имя в with
заявлением.
Например, с помощью подключения к базе данных в with
утверждением может дать вам объект курсора:
with database_connection as cursor:
cursor.execute(sql_query)
Файловые объекты возвращаются сами по себе, это позволяет как открыть объект файла, так и использовать его в качестве диспетчера контекста в одном выражении:
with open(filename) as open_file:
file_contents = open_file.read()
Написание собственного менеджера контекста
Менеджер контекста является любой объект , который реализует два магических методов __enter__()
и __exit__()
(хотя он может реализовать другие методы, а):
class AContextManager():
def __enter__(self):
print("Entered")
# optionally return an object
return "A-instance"
def __exit__(self, exc_type, exc_value, traceback):
print("Exited" + (" (with an exception)" if exc_type else ""))
# return True if you want to suppress the exception
Если контекст выходит с исключением, информация о том , что исключение будет передан как тройной exc_type
, exc_value
, traceback
(это те же переменные , как возвращаемое sys.exc_info()
функции). Если контекст выходит нормально, все три из этих аргументов не будет None
.
Если исключение происходит , и передается в __exit__
метод, метод может возвращать True
, чтобы подавить исключение, или исключение будет вновь поднят в конце __exit__
функции.
with AContextManager() as a:
print("a is %r" % a)
# Entered
# a is 'A-instance'
# Exited
with AContextManager() as a:
print("a is %d" % a)
# Entered
# Exited (with an exception)
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# TypeError: %d format: a number is required, not str
Следует отметить , что во втором примере , даже если исключение происходит в середине тела с-заявлением __exit__
обработчик еще запускается на выполнение, до того , как исключение распространяется на внешнюю область.
Если вам необходим только __exit__
метод, вы можете вернуть экземпляр менеджера контекста:
class MyContextManager:
def __enter__(self):
return self
def __exit__(self):
print('something')
Написание собственного менеджера контекста с использованием синтаксиса генератора
Также можно написать менеджер контекста с использованием синтаксиса генератора благодаря contextlib.contextmanager
декоратора:
import contextlib
@contextlib.contextmanager
def context_manager(num):
print('Enter')
yield num + 1
print('Exit')
with context_manager(2) as cm:
# the following instructions are run when the 'yield' point of the context
# manager is reached.
# 'cm' will have the value that was yielded
print('Right in the middle with cm = {}'.format(cm))
производит:
Enter
Right in the middle with cm = 3
Exit
Декоратор упрощает задачу написания менеджера контекста путем преобразования генератора в один. Все , что прежде , чем выражение выхода становится __enter__
метод, значение дало становится значение , возвращаемым генератор (который может быть связан с переменным в отчете с), и все , что после того, как выражение выхода становится __exit__
методом.
Если исключение должно быть обработано менеджером контекста, A try..except..finally
-блок может быть записана в генераторе и каких - либо исключений , затронутые в with
-блоком будут обработаны этим блоком исключений.
@contextlib.contextmanager
def error_handling_context_manager(num):
print("Enter")
try:
yield num + 1
except ZeroDivisionError:
print("Caught error")
finally:
print("Cleaning up")
print("Exit")
with error_handling_context_manager(-1) as cm:
print("Dividing by cm = {}".format(cm))
print(2 / cm)
Это производит:
Enter
Dividing by cm = 0
Caught error
Cleaning up
Exit
Несколько контекстных менеджеров
Вы можете открыть несколько менеджеров контента одновременно:
with open(input_path) as input_file, open(output_path, 'w') as output_file:
# do something with both files.
# e.g. copy the contents of input_file into output_file
for line in input_file:
output_file.write(line + '\n')
Это имеет тот же эффект, что и вложенные контекстные менеджеры:
with open(input_path) as input_file:
with open(output_path, 'w') as output_file:
for line in input_file:
output_file.write(line + '\n')
Управление ресурсами
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.open_file = open(self.filename, self.mode)
return self.open_file
def __exit__(self, *args):
self.open_file.close()
__init__()
метод устанавливает объект, в данном случае настраивает имя файла и режим открытия файла. __enter__()
открывает и возвращает файл и __exit__()
просто закрывает его.
Используя эти магические методы ( __enter__
, __exit__
) позволяет реализовать объекты , которые могут быть легко использованы with
с утверждением.
Используйте класс File:
for _ in range(10000):
with File('foo.txt', 'w') as f:
f.write('foo')