Менеджеры контекста файлов питон

Контекстный менеджер в Python

Менеджер контекста — это объект, определяющий контекст выполнения в операторе with.

Давайте начнем с простого примера, чтобы понять концепцию менеджера контекста.

Предположим, что у yас есть файл data.txt, в котором содержится целое число 100.

Теперь напишем программу, которая читает файл data.txt, преобразует его содержимое в число и выводит результат на стандартный вывод:

f = open('data.txt') data = f.readlines() # преобразует содержимое в целое число и выводит его print(int(data[0])) f.close()

Однако что если в файле data.txt будут содержаться данные, которые не нельзя преобразовать в целое число? В таком случае возникнет исключение ValueError.

Например, если в файл data.txt записать строку ‘100’ вместо числа 100, вы получите следующую ошибку:

ValueError: invalid literal for int() with base 10: "'100'"

Из-за этого исключения Python может не закрыть файл должным образом.

Чтобы исправить такое поведение, можно воспользоваться оператором try. except. finally :

try: f = open('data.txt') data = f.readlines() # преобразует содержимое в целое число и выводит его print(int(data[0])) except ValueError as error: print(error) finally: f.close()

Поскольку код в блоке finally всегда выполняется, программа всегда будет правильно закрывать файл.

Это решение работает как надо, но оно слишком многословное.

Поэтому в Python есть способ автоматически закрыть файл после завершения его обработки — и он менее многословный.

Здесь в игру вступают менеджеры контекста.

Ниже показано, как использовать менеджер контекста для обработки файла data.txt:

with open('data.txt') as f: data = f.readlines() print(int(data[0]) 

В этом примере мы используем функцию open() с оператором with . После блока with Python автоматически закроется.

Оператор with

Синтаксис

with context as ctx: # используем объект # очищаем контекст

Как это работает

  • Когда Python встречает оператор with , он создает новый контекст. При желании контекст может возвращать объект.
  • После блока with Python автоматически очищает контекст.
  • Область видимости ctx имеет ту же область видимости, что и оператор with . Это означает, что вы можете обращаться к ctx как внутри оператора with , так и после него.

Ниже показано, как получить доступ к переменной f после оператора with :

with open('data.txt') as f: data = f.readlines() print(int(data[0])) print(f.closed) # Вывод: True

Протокол контекстного менеджера

Контекстные менеджеры Python работают на основе протокола контекстного менеджера.

Протокол менеджера контекста включает следующие методы:

  • __enter__() — устанавливает контекст и, по желанию, возвращать некоторый объект.
  • __exit__() — очищает объект.

Если вы хотите, чтобы класс поддерживал протокол контекстного менеджера, вам необходимо реализовать эти два метода.

Предположим, что у нас есть некий класс ContextManager , поддерживающий протокол контекстного менеджера.

Вот, как можно использовать этот класс:

with ContextManager() as ctx: # что-то делаем # закончили с контекстом

Когда вы используете класс ContextManager с оператором with , Python неявно создает экземпляр класса — instance — и автоматически вызывает метод __enter__() на этом экземпляре.

Метод __enter__() может по желанию возвращать объект. Если это так, Python присваивает возвращаемый объект ctx .

Обратите внимание, что ctx ссылается на объект, возвращаемый методом __enter__() . Он не ссылается на экземпляр класса ContextManager .

Если внутри блока with или после блока with возникает исключение, Python вызывает метод __exit__() на объекте экземпляра.

Функционально оператор with эквивалентен конструкции try. finally :

instance = ContextManager() ctx = instance.__enter__() try: # что-то делаем с txt finally: # закончили с контекстом instance.__exit__() 

Метод __enter__()

В методе __enter__() можно выполнить необходимые действия по настройке контекста.

При необходимости вы можете вернуть объект из метода __enter__() .

Метод __exit__()

Python всегда выполняет метод __exit__() , даже если в блоке with возникает исключение.

Метод __exit__() принимает три аргумента: тип исключения, значение исключения и объект трассировки. Все эти аргументы будут равны None , если исключение не произошло.

def __exit__(self, ex_type, ex_value, ex_traceback): . 

Метод __exit__() возвращает логическое значение: True или False .

Если возвращаемое значение равно True , Python заглушит исключение.

Как можно использовать контекстный менеджер

В этой статье мы уже выяснили, что контекстный менеджер можно использовать для автоматического открытия и закрытия файлов.

Давайте разберемся, в каких случаях еще можно использовать контекстный менеджер. Вот некоторые из них:

1) Закрытие — открытие

Если вы хотите открывать и закрывать ресурс автоматически, вы можете использовать контекстный менеджер.

Например, вы можете открыть сокет и закрыть его с помощью контекстного менеджера.

2) Заблокировать — разблокировать

Контекстные менеджеры помогут вам более эффективно управлять блокировками объектов.

3) Запустить — остановить

Контекстные менеджеры помогут вам работать со сценариями, требующими запуска и остановки.

Например, можно использовать контекстный менеджер для запуска таймера и его автоматической остановки.

4) Изменить — сбросить

Контекстные менеджеры могут работать со сценариями изменения и сброса.

Например, вашему приложению необходимо подключиться к нескольким источникам данных. И у него есть соединение по умолчанию.

Вот алгоритм для подключения к другому источнику данных:

  • Используйте контекстный менеджер для изменения соединения по умолчанию на новое.
  • Работайте с новым соединением.
  • После завершения работы с новым соединением верните его обратно к соединению по умолчанию.

Создание протокола контекстного менеджера

Ниже показана простая реализация функции open() с использованием протокола контекстного менеджера:

class File: def __init__(self, filename, mode): self.filename = filename self.mode = mode def __enter__(self): print(f'Opening the file .') self.__file = open(self.filename, self.mode) return self.__file def __exit__(self, exc_type, exc_value, exc_traceback): print(f'Closing the file .') if not self.__file.closed: self.__file.close() return False with File('data.txt', 'r') as f: print(int(next(f))) 

Как это работает

  • Инициализируем имя файла и режим в методе __init__() .
  • Открываем файл в методе __enter__() и возвращаем объект файла.
  • Закрываем файл, если он открыт, в методе __exit__() .

Реализации шаблона запуска и остановки с помощью контекстного менеджера

Давайте создадим класс Timer , который поддерживает протокол контекстного менеджера:

from time import perf_counter class Timer: def __init__(self): self.elapsed = 0 def __enter__(self): self.start = perf_counter() return self def __exit__(self, exc_type, exc_value, exc_traceback): self.stop = perf_counter() self.elapsed = self.stop - self.start return False 

Как это работает

  • Импортируем perf_counter из модуля time .
  • Запускаем таймер в методе __enter__() .
  • Останавливаем таймер в методе __exit__() и верните прошедшее время.

Теперь вы можете использовать класс Timer для измерения времени, необходимого для вычисления числа Фибоначчи, равного 1000, один миллион раз:

def fibonacci(n): f1 = 1 f2 = 1 for i in range(n-1): f1, f2 = f2, f1 + f2 return f1 with Timer() as timer: for _ in range(1, 1000000): fibonacci(1000) print(timer.elapsed)

Что нужно запомнить

  • Используйте менеджеры контекстов для определения контекстов времени выполнения при выполнении в операторе with .
  • Для поддержки протокола менеджера контекста нужно реализовать методы __enter__() и __exit__() .

СodeСhick.io — простой и эффективный способ изучения программирования.

2023 © ООО «Алгоритмы и практика»

Источник

Читайте также:  Java use system variables
Оцените статью