При работе с некоторым ресурсом, в конце этот ресурс должен быть
освобожден, при работе с исключениями мы делаем это в блоке
finally
этот блок выполнятся в любом случае, так открыв
дескриптор файла его можно закрыть в финальном блоке.
Это освобождение ресурса является настолько необходимым, что для него существует специальный отдельный синтаксис, так называемый менеджер контекста.
with
- получает ресурс и работает с ним под неким псевдонимом,
и по завершению работы ресурс сам себя завершит.
Пример работы исключения против менеджера контекста with
:
- Первый пример
# Пример открытия фалйа и его закрытие при помощи исключений
try:
file = open('file.txt', 'r')
print("Пример с исключениями")
for line in file:
print(line)
except Exception:
raise
finally:
file.close()
- Второй пример
# Тот же пример но с менеджером контекста
try:
with open('file.txt', 'r') as file:
print("Пример с менеджером контекста")
for line in file:
print(line)
except Exception:
raise
По сути менеджер контекста это класс который реализует 2 магических
метода, __enter__()
и __exit__()
.
__enter__()
- метод что принимает входящее значение, в данном
случае это open('file.txt', 'r')
открытый на запись дескриптор
файла, а возвращает этот метод то что попадет в псевдоним as file
__exit__()
- по завершению работы блока with
вызывается метод
__exit__()
в котором можно закрыть дескриптор класса, и происходит
это даже в том случае если в процессе работы возникло исключение.
Функция выхода имеет свои аргументы def __exit__(self, exc_type, exc_val, exc_tb):
где аргумент exc_type
держит в себе все исключения которые могли
возникнуть, или содержит None
тут мы можем перехватить это исключение,
если все шло по плану то исключений не будет, если ошибка возникла
то тут мы его и увидим.
В след примере мы создаем 2 вектора и с помощью оператора менеджер
контекста складываем эти вектора вместе, случае если произошла
ошибка мы отловим ее в __exit__
и не будет сохранять новый
высчитанный вектор, а если ошибки не было то мы сохраним вычисления.
class VectorTesting:
def __init__(self, vector):
self.__vector = vector
def __enter__(self):
self.temp = self.__vector[:]
return self.temp
# return False - обработка исключений передается на более
# верхний уровень. True - будет уходить на ур выше.
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
self.__vector[:] = self.temp
return False
# Пример работы с вектором
vector_1 = [1, 2, 3]
vector_2 = [5, 5, 5]
print('vector_1 = ', vector_1)
print('vector_2 = ', vector_2)
# Складываем 2 вектора одинаковой длинны
try:
with VectorTesting(vector_1) as vector_temp:
for i in range(len(vector_temp)):
vector_temp[i] += vector_2[i]
except Exception as e:
print('Произошло исключение = ', e)
print('Новый vector_1 = ', vector_1)