Документация по Python

Менеджеры контекста (оператор «with»)

В: Документация по Python

Введение в контекстные менеджеры и оператор 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') 
Еще от кодкамп
Замечательно! Вы успешно подписались.
Добро пожаловать обратно! Вы успешно вошли
Вы успешно подписались на кодкамп.
Срок действия вашей ссылки истек.
Ура! Проверьте свою электронную почту на наличие волшебной ссылки для входа.
Успех! Ваша платежная информация обновлена.
Ваша платежная информация не была обновлена.