4.1 Закриття файла і обробка помилок
Іноді при читанні файла виникають помилки або винятки, і файл залишається
незакритим. Це потенційно може призвести до витоку пам'яті або
витоку file handler'ів
. Тому роботу з файлом потрібно обертати в
блок try-except
.
Допустимо, у вас був код:
file = open('example.txt', 'a') # Відкриваємо файл
file.write("This is a new line added to the file.")
file.close() # Закриваємо файл
Його потрібно загорнути в блок try-except
:
try:
file = open('example.txt', 'a') # Відкриваємо файл
file.write("This is a new line added to the file.")
file.close() # Закриваємо файл
except FileNotFoundError:
print("File not found")
file.close() # Закриваємо файл
Щоб двічі не писати метод close()
, можна винести його в блок
finally
:
try:
file = open('example.txt', 'a') # Відкриваємо файл
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
file.close() # Закриваємо файл
Цей код виглядає красиво, але… не працює, оскільки змінна file
визначена тільки в блоці try
і недоступна з блоків except
і finally
.
Тому нам потрібно визначити змінну на рівень вище:
file = None
try:
file = open('example.txt', 'a') # Відкриваємо файл
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
file.close() # Закриваємо файл
Це рішення краще, але у нього теж є недоліки. Наприклад, якщо файл
так і не буде відкритий, то в змінній file
залишиться None
. І тоді
спроба закрити файл викличе помилку — буде спроба звернутися до
методу close()
у неіснуючого об'єкта.
Тому нам потрібно додати перевірку перед викликом методу close()
:
file = None
try:
file = open('example.txt', 'a') # Відкриваємо файл
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
if file:
file.close() # Закриваємо файл
Якщо ви здивовані тим, що 3 рядки коду перетворилися в 9, то ви не самі. На щастя, вже є готове рішення для цієї проблеми, про яке ми поговоримо далі.
4.2 Оператор with
Оператор with
в Python надає зручний спосіб управління
ресурсами, такими як файли, забезпечуючи їх автоматичне закриття
після завершення блоку with
. Це спрощує код і запобігає витокам
ресурсів, таких як незакриті файли.
Загальний синтаксис оператора with
:
with вираз as змінна:
робота з змінною
Оператор with
використовується для
обгортання виконання блоку коду менеджером контексту. При
використанні оператора with
Python автоматично
викликає методи __enter__()
і
__exit__()
об'єкта менеджера контексту, що спрощує
управління ресурсами.
Приклад використання with
для роботи з файлами:
with open('example.txt', 'w') as file:
file.write("Hello, World!\n")
file.write("This is a test file.\n")
У цьому прикладі файл example.txt
відкривається в режимі запису, а ім'я
файла зв'язується з змінною file
. Блок коду
всередині with
автоматично закриває файл після виконання всіх операцій запису.
4.3 Автоматичне закриття файла
Однією з головних переваг використання
оператора with
є автоматичне
закриття файла після завершення блоку коду. Це відбувається
навіть у випадку виникнення винятку, що робить
код більш безпечним і надійним.
Приклад автоматичного закриття файла:
try:
with open('example.txt', 'w') as file:
file.write("Hello, World!\n")
file.write("This is a test file.\n")
# Виняток, щоб перевірити, що файл все одно закриється
raise Exception("Something went wrong")
except Exception as e:
print(f"Caught an exception: {e}")
# У цьому місці файл вже закритий
Завжди використовуйте оператор with
при
роботі з файлами. Це просто і дозволяє уникнути багатьох помилок.
4.4 Під капотом оператора with
В основі роботи оператора with
лежать методи __enter__()
і
__exit__()
, які повинні бути реалізовані в класі, що використовується
як менеджер контексту.
Для того, щоб об'єкт міг використовуватись з
оператором with
, він повинен реалізовувати методи __enter__()
і
__exit__()
. Ці методи визначають поведінку при вході і
виході з контексту.
Метод __enter__()
Метод __enter__()
викликається при вході в блок with
.
Він виконує ініціалізацію ресурсу і повертає об'єкт, який
буде прив'язаний до змінної, вказаної після as
.
Метод __exit__()
Метод __exit__()
викликається при виході з блоку with
. Він виконує
завершальні дії, такі як звільнення ресурсів або закриття
файлів. Метод приймає три аргумента: exc_type
, exc_val
і exc_tb
,
які містять інформацію про виняток, якщо він стався.
-
exc_type
: тип винятку (наприклад,ZeroDivisionError
). -
exc_val
: значення винятку (наприклад, повідомлення про помилку). exc_tb
: трасування стека винятку.
Якщо метод __exit__()
повертає True
,
то виняток буде придушено. Якщо повертає
False
, виняток буде повторно викликано.
Приклад:
class MyContextManager:
def __enter__(self):
print("Entering the context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(f"An exception occurred: {exc_type}, {exc_val}")
return False # Виняток не придушується
with MyContextManager() as manager:
print("Inside the with block")