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

Введение

Примеры

  • 26

    Введение в контекстные менеджеры и оператор 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.

  • 2

    Присвоение цели

    Многие контекстные менеджеры возвращают объект при вводе. Вы можете назначить этот объект на новое имя в with заявлением.

    Например, с помощью подключения к базе данных в with утверждением может дать вам объект курсора:

     with database_connection as cursor:
        cursor.execute(sql_query)
    
     

    Файловые объекты возвращаются сами по себе, это позволяет как открыть объект файла, так и использовать его в качестве диспетчера контекста в одном выражении:

     with open(filename) as open_file:
        file_contents = open_file.read() 
  • 24

    Написание собственного менеджера контекста

    Менеджер контекста является любой объект , который реализует два магических методов __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') 
  • 10

    Написание собственного менеджера контекста с использованием синтаксиса генератора

    Также можно написать менеджер контекста с использованием синтаксиса генератора благодаря 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 
  • 7

    Несколько контекстных менеджеров

    Вы можете открыть несколько менеджеров контента одновременно:

     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') 
  • 2

    Управление ресурсами

     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') 

Синтаксис

Параметры

Примечания