FileNotFoundError в Python: файл не нашёлся
open('нет-такого-файла.txt') — и программа падает. Ловим FileNotFoundError, читаем имя файла из объекта ошибки и создаём файлы прямо в примерах.
Редакция Питоники
Работа с файлами — первое место, где программа встречается с внешним миром: файл кто-то мог переименовать, удалить, назвать по-другому. Ошибки тут не баги кода, а свойства реальности, и для них у Python есть целое семейство — OSError, о котором говорилось в уроке об иерархии. Его самый известный ребёнок — FileNotFoundError: файл не нашёлся.
Файл, которого нет
f = open('нет-такого-файла.txt', encoding='utf-8')
В сообщении три части: тип ошибки, системный код и путь, по которому искали. Код меняется от системы к системе, поэтому в собственных сообщениях его не печатают — а вот путь из e.filename стабилен и годится для журналов. Сама ошибка честная: ни файла рядом, ни файла по указанному пути не нашлось, и придумывать содержимое open не станет.
Ловим FileNotFoundError
try:
open('нет-такого-файла.txt', encoding='utf-8')
except FileNotFoundError as e:
print('поймали', type(e).__name__)
print('файл:', e.filename)
поймали FileNotFoundError файл: нет-такого-файла.txt
Ловится ошибка как любая другая, а объект несёт полезные атрибуты: filename — путь, по которому искали. Этого хватает, чтобы не падать молча, а сообщить пользователю, какого именно файла не хватило: «файл отчёта за март не найден» звучит лучше, чем traceback на полэкрана.
try:
open('нет.txt', encoding='utf-8')
except OSError as e:
print('OSError поймал за ребёнка:', type(e).__name__)
OSError поймал за ребёнка: FileNotFoundError
Файл не нашёлся, а напечатанное имя — FileNotFoundError: родительская ветка перехватила детскую ошибку, сохранив её настоящий тип. Ветку по OSError пишут, когда реакция одна на все системные проблемы; когда «нет файла» нужно отличить от «нет прав», ловят точное имя — FileNotFoundError или PermissionError.
Открытый файл положено закрывать даже при сбое — за это отвечает with, а про его связь с finally из шестого урока стоит перечитать на досуге. Дальше — практика: примеры этого урока сами создают себе файлы, читают их и убирают за собой.
Самодостаточный пример: создать, прочитать, убрать
import os
path = 'urok13_notes.txt'
with open(path, 'w', encoding='utf-8') as f:
f.write('первая строка\nвторая строка\n')
with open(path, encoding='utf-8') as f:
content = f.read()
print('прочитано', len(content), 'символов')
print(content.splitlines()[0])
os.remove(path)
print('файл существует?', os.path.exists(path))
прочитано 28 символов первая строка файл существует? False
Такой порядок — создать, использовать, удалить — делает пример честным на любой машине: он не зависит от файлов, которых у читателя может не быть, и не оставляет мусора. len(content) насчитал двадцать восемь символов — две строки по тринадцать плюс два перевода строки. Метод splitlines() рвёт содержимое на строки без хвостовых переводов, а os.path.exists после удаления подтверждает: файла больше нет.
exists: спросить до действия
import os
path = 'urok13_config.txt'
if os.path.exists(path):
print('конфиг на месте, читаем')
else:
print('конфига нет — работаем с умолчаниями')
try:
open(path, encoding='utf-8')
except FileNotFoundError:
print('open подтвердил: файла нет')
конфига нет — работаем с умолчаниями open подтвердил: файла нет
os.path.exists отвечает на вопрос «есть ли файл» без всяких исключений — проверка перед действием, стиль LBYL. У неё есть тонкость, из четырнадцатого урока об EAFP: проверка exists отвечает на вопрос до действия, а try с FileNotFoundError — после него: между проверкой и открытием файл может исчезнуть. В одиночном скрипте разница незаметна, но принцип стоит знать заранее.
На практике чаще всего нужен каркас «если файл есть — прочитать, если нет — умолчание». Он собирается через try с запасным значением:
import os
def read_config(path, default='пусто'):
try:
with open(path, encoding='utf-8') as f:
return f.read().strip()
except FileNotFoundError:
return default
path = 'urok13_conf.txt'
print('конфиг:', repr(read_config(path)))
with open(path, 'w', encoding='utf-8') as f:
f.write('режим=тёмный\n')
print('конфиг:', repr(read_config(path)))
os.remove(path)
конфиг: 'пусто' конфиг: 'режим=тёмный'
Функция возвращает содержимое файла или запасное значение — и вызывающий код не знает про системные ошибки вовсе. strip() срезает хвостовой перевод строки, repr в печати показывает границы значения, чтобы пустую строку не спутать с пустотой в терминале.
Файл — внешние данные, и внешние данные не обязаны существовать: FileNotFoundError делает это свойство видимым и управляемым. Два стиля обхода — спросить заранее или действовать и ловить — встретятся ещё раз в уроке об EAFP, а практику с грязными файлами ждите в финальном проекте раздела.
Сначала предскажи ответ в голове — это главный навык программиста.
try:
open('нет.txt', encoding='utf-8')
except OSError as e:
print('поймано:', type(e).__name__)
import os
path = 'demo.txt'
print(os.path.exists(path))
with open(path, 'w', encoding='utf-8') as f:
f.write('привет')
print(os.path.exists(path))
os.remove(path)
def read_or(path, default='нет'):
try:
with open(path, encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
return default
print(read_or('отсутствует.txt'))
1. Кто родитель FileNotFoundError в иерархии исключений?
2. Где в объекте FileNotFoundError удобнее всего взять путь к файлу?
3. Что вернёт os.path.exists('файл.txt') после os.remove('файл.txt')?
4. Почему в каркасе read_config ловят FileNotFoundError, а не используют os.path.exists?
5. Скрипт лежит в папке projects, файл data.txt — рядом с ним, запуск идёт из домашней папки. Что произойдёт при open('data.txt')?
6. Когда уместна ветка except OSError вместо точного FileNotFoundError?
Напиши функцию read_or(path, default): она возвращает содержимое файла через try с open и read, а при FileNotFoundError возвращает default. Проверь её на отсутствующем файле 'нет-данных.txt' с умолчанием «записей нет» и напечатай результат.
Почему Python пишет FileNotFoundError, хотя файл существует?
open ищет файл относительно рабочей папки процесса, а не папки, где лежит скрипт. Запуск из другого каталога меняет поиск — и существующий файл становится «несуществующим». Проверьте рабочую папку через os.getcwd или укажите абсолютный путь.
Урок 2: try и except — ловим ошибкуКак безопасно открыть файл, которого может не быть?
Обернуть open в try и поймать FileNotFoundError: except FileNotFoundError as e, где e.filename содержит путь. Для случая «нет файла — возьми умолчание» напишите функцию с try и return default — вызывающий код не будет знать про системные ошибки.
Чем os.path.exists лучше try с FileNotFoundError?
exists отвечает на вопрос заранее и без исключений — удобно для простых сценариев. Но между проверкой и открытием файл может исчезнуть: try решает по факту открытия. Проверка — стиль LBYL, ловля — EAFP; в учебнике им посвящён отдельный урок.
Урок 14: EAFP против LBYLЧто означает [Errno 2] в сообщении FileNotFoundError?
Это системный код ошибки «no such file or directory». Число зависит от платформы, поэтому в своих сообщениях его не печатают — берите путь из e.filename: он одинаков везде и годится для журналов.
Урок 9: иерархия исключенийПонравился урок? Сошлитесь на него
«Его самый известный ребёнок — FileNotFoundError: файл не нашёлся.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Исключения · Урок 9
Иерархия исключений Python: кто чей родитель
ZeroDivisionError, KeyError, FileNotFoundError — не набор случайных имён, а семейное дерево. Разбираемся, кто чей родитель и почему ловля родителя ловит детей.
Исключения · Урок 6
else и finally: два хвоста try
Два хвоста try: else работает при успехе, finally — при любом исходе. Разбираем порядок try, except, else, finally и место каждого блока.
Исключения · Урок 14
EAFP против LBYL в Python: два стиля проверки
Look Before You Leap — смотри, прежде чем прыгнуть; Easier to Ask Forgiveness than Permission — проще извиниться, чем спросить. Одна задача, два стиля, один вердикт.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Компиляция · Урок 2
pyc-файл: устройство скомпилированного файла Python
pyc — это кодовый объект, упакованный в байты: 16 байт заголовка с magic-числом, временем и размером исходника, дальше marshal-блок с байткодом. Разбираем файл до последнего байта.
pyc файлустройство pyc файла
pytest · Урок 9
Проверка исключений: pytest.raises
Хорошая функция на плохом входе не молчит, а бросает исключение — и тест обязан это проверять. pytest.raises делает это одной строкой внутри with.
pytest.raises проверка исключенийpytest проверка исключения
Исключения · Урок 11
KeyError в Python: ошибки словарей
Словарь не нашёл ключ — и честно сказал об этим KeyError. Разбираем объект ошибки, читаем из него ключ и выбираем между get, in и честным исключением.
python ошибка keyerrorpython проверка ключа in