# обработка файлов
# 1) не используется с оператором
файл
=
open
(
`file_path`
,
`w`
)
файл
. write (
`hello world!`
)
файл
. закрыть ()
# 2) не использовать с оператором
файл
=
open
(
`file_path`
,
`w`
)
try
:
файл
. write (
`hello world`
)
наконец
:
file
.close ()
# использовать с оператором
with
open
(
` file_path`
,
`w`
файл
:
файл
. write (
`hello world!`
)
Обратите внимание, что в отличие от первых двух реализаций нет необходимости вызывать file.close()
при использовании инструкции with
. Сам оператор with
обеспечивает правильное получение и освобождение ресурсов. Исключение при file.write()
в первой реализации может помешать правильному закрытию файла, что может привести к нескольким ошибкам в коде, т.е. многие изменения в файлах не вступят в силу, пока файл не будет должным образом закрыты.
Второй подход в приведенном выше примере позаботится обо всех исключениях, но использование инструкции with
делает код компактным и более читабельным. Таким образом, оператор with
помогает избежать ошибок и утечек, гарантируя, что ресурс освобождается должным образом, когда код, использующий ресурс, полностью выполнен. Оператор with
обычно используется с файловыми потоками, как показано выше, а также с блокировками, сокетами, подпроцессами, telnet и т. д.
Поддержка оператора with в пользовательских объектах
В open()
нет ничего особенного, что делает его пригодным для использования с оператором with
with
, и та же функциональность может предоставляться в пользовательских объектах. Поддержка утверждений с
в ваших объектах гарантирует, что вы никогда не оставите открытыми ресурсы.
Чтобы использовать оператор с
в пользовательских объектах, вам нужно всего лишь добавить __enter __()
и __exit __()
к методам объекта. Для дальнейшего пояснения рассмотрим следующий пример.
# простой модуль записи файлов
class
MessageWriter (
object
):
def
__ init __ (
self
, file_name):
self
. имя_файла
=
имя_файла
def
__ введите __ (
self
):
self
.
файл
=
открыть
(
self
. имя_файла,
` w`
)
return
self
.
файл
def
__ exit __ (
self
):
self
.
файл
. close ()
# использовать с оператором с MessageWriter
с MessageWriter (
`my_file.txt`
) как xfile:
xfile.write (
`hello world`
)
Давайте посмотрим на приведенный выше код. Если вы заметили, что ключевое слово with
следует за конструктором MessageWriter
. Как только выполнение входит в контекст with
путем утверждения объекта MessageWriter
, создается объект, а затем python вызывает метод __enter __()
. В этом __enter __()
инициализируйте ресурс, который вы хотите использовать в объекте. Этот __enter __ ()
всегда должен возвращать дескриптор полученного ресурса.
Что такое дескрипторы ресурсов?
Это дескрипторы, предоставляемые операционной системой для доступа к запрошенным ресурсам. В следующем блоке кода file
является дескриптором ресурса файлового потока.
file
=
open
(
`hello.txt `
)
В предоставленном примере MessageWriter
__enter __()
метод __enter __()
создает дескриптор файла и возвращает его. Имя xfile
используется здесь для обозначения файлового дескриптора, возвращаемого методом __enter __()
. Блок кода, использующий полученный ресурс, помещается внутрь блока операторов with
. После выполнения кода внутри блока with
метод __exit __()
становится __exit __()
. Все полученные ресурсы освобождаются в __exit__()
. Вот как мы используем утверждение with
с определяемыми пользователем объектами.
Этот интерфейс метода — __enter __ ()
и __exit __ ()
, обеспечивающий поддержку оператора with
в пользовательских объектах, называется Context Manager .
Модуль contextlib
Контекстный менеджер на основе классов, как показано выше, — не единственный способ поддержки оператора with
в пользовательских объектах. Модуль contextlib
предоставляет дополнительные абстракции, построенные на базовом интерфейсе. менеджер контекста. Вот как мы можем переписать менеджер контекста для объекта MessageWriter
, используя модуль contextlib
.
< / p>
< / p> from
contextlib
import
менеджер контекста
класс
MessageWriter (
object
):
def
__ init __ (
self
, имя файла):
self
. имя_файла
=
имя файла
@ contextmanager
def
open_file (
self
):
попробовать
:
файл
=
open
(
self
.file_name,
`w`
)
yield
file
наконец
:
файл
. close ()
# use
message_writer
=
MessageWriter (
`hello.txt`
)
с message_writer.open_file() as my_file:
my_file.write (
`hello world`
)
В этом примере кода из-за оператора генератором функций .
Когда open_file()
этот open_file()
, он создает дескриптор ресурса с именем file
. Затем этот дескриптор ресурса передается вызывающему объекту и представляется здесь переменной my_file
. После выполнения кода внутри блока with
управление программой возвращается обратно к функции open_file()
. Функция open_file()
возобновляет свое выполнение и выполняет код, следующий за оператором yield
. Эта часть кода, которая появляется после оператора yield
, освобождает результирующие ресурсы. @contextmanager
здесь находится