Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 13 из 20 Начальный 15 мин 100 XP

FileNotFoundError в Python: файл не нашёлся

open('нет-такого-файла.txt') — и программа падает. Ловим FileNotFoundError, читаем имя файла из объекта ошибки и создаём файлы прямо в примерах.

Редакция Питоники

Работа с файлами — первое место, где программа встречается с внешним миром: файл кто-то мог переименовать, удалить, назвать по-другому. Ошибки тут не баги кода, а свойства реальности, и для них у Python есть целое семейство — OSError, о котором говорилось в уроке об иерархии. Его самый известный ребёнок — FileNotFoundError: файл не нашёлся.

Файл, которого нет

открытие несуществующего файла
f = open('нет-такого-файла.txt', encoding='utf-8')
Программа падает с последней строкой traceback: FileNotFoundError: [Errno 2] No such file or directory: 'нет-такого-файла.txt'. Число в скобках — код системной ошибки, он различается между платформами; надёжное имя файла — дальше, в кавычках.

В сообщении три части: тип ошибки, системный код и путь, по которому искали. Код меняется от системы к системе, поэтому в собственных сообщениях его не печатают — а вот путь из 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'))
Проверь себя
0 / 6

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?

Карточки терминов
Запомнено: 0 / 7
Практика

Напиши функцию read_or(path, default): она возвращает содержимое файла через try с open и read, а при FileNotFoundError возвращает default. Проверь её на отсутствующем файле 'нет-данных.txt' с умолчанием «записей нет» и напечатай результат.

practice.py
Вопросы и ответы по уроку

Почему 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-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.