Короткий ответ
Чтобы проверить, существует ли файл, используй os.path.isfile() из стандартной библиотеки. Функция возвращает True, если путь указывает на существующий обычный файл, и False во всех остальных случаях. Для несуществующего пути она никогда не выбрасывает ошибку.
Пути считаются относительно папки, в которой запущена программа (текущей рабочей директории), если только ты не передаёшь абсолютный путь вроде /home/ada/notes.txt или C:\Users\Ada\notes.txt.
Дальше на этой странице: другие способы задать тот же вопрос, чем они различаются и случай, когда задавать его вообще не стоит.
os.path.exists, isfile и isdir
В модуле os.path есть три родственные проверки, и отвечают они на немного разные вопросы:
| Функция | Возвращает True, когда путь указывает на |
|---|---|
os.path.exists(p) | что угодно существующее: файл, папку или рабочую символическую ссылку |
os.path.isfile(p) | существующий обычный файл |
os.path.isdir(p) | существующую папку |
Разница проявляется, как только в деле появляется папка:
Вывод:
reports True False True
reports/summary.txt True True False
reports/old.txt False False False
os.path.exists("reports") равен True, хотя reports это папка. Если дальше код собирается читать путь как файл, exists будет неправильной проверкой: папка её пройдёт, а open("reports") потом упадёт (с IsADirectoryError в Linux и macOS, с PermissionError в Windows). Бери isfile, когда имеешь в виду файл, и isdir, когда имеешь в виду папку.
Ещё две детали про эти три функции os.path:
- Символическая ссылка разыменовывается. Ссылка на настоящий файл считается файлом; битая ссылка (её цель удалена) даёт
Falseво всех трёх функциях. - Если у Python нет прав заглянуть в папку, проверка возвращает
False, а не выбрасываетPermissionError.Falseозначает «не найдено или недоступно», а не строго «не существует».
Проверка через pathlib
pathlib это объектно-ориентированный API для путей из стандартной библиотеки, и большая часть нового кода использует именно его. У объекта Path те же три проверки в виде методов:
Path.exists(), Path.is_file() и Path.is_dir() ведут себя так же, как os.path.exists, isfile и isdir. Оператор / соединяет части пути с правильным для операционной системы разделителем, поэтому folder / "summary.txt" одинаково работает в Windows, macOS и Linux. Как читать и записывать файлы через объекты Path, смотри на странице про работу с файлами.
Что выбрать? Если остальной код уже передаёт объекты Path, оставайся с pathlib. Если ты работаешь с обычными строками и функциями os, os.path.isfile ничуть не хуже. Ответы у них одинаковые.
Почему проверять заранее бывает ошибкой
Чаще всего существование файла проверяют, чтобы потом его открыть:
import os
if os.path.isfile("config.txt"):
with open("config.txt") as f: # the file might be gone by now
settings = f.read()
else:
settings = ""
Здесь есть щель. Между вызовом isfile() и вызовом open() другая программа, другой поток или сам пользователь может удалить или переименовать файл. Проверка сказала True, открытие всё равно не удалось, и программа всё равно падает с FileNotFoundError. Такая щель называется состоянием гонки (race condition), или «time of check to time of use». На собственном ноутбуке это редкость, а на загруженном сервере, где несколько процессов работают с одной папкой, случается гораздо чаще.
Решение: убрать отдельную проверку и сделать проверкой сам open(). Когда файла нет, Python выбрасывает FileNotFoundError, а ты обрабатываешь его в except:
Вывод:
theme=dark
(no settings file, using defaults)
Теперь операция одна, а не две, и гоняться не с чем. В сообществе Python у этого стиля есть имя: EAFP, «easier to ask forgiveness than permission» (проще попросить прощения, чем разрешения). Противоположный стиль, когда сначала проверяют, называется LBYL, «look before you leap» (посмотри, прежде чем прыгать). Для работы с файлами Python склоняется к EAFP, потому что только операционная система знает ответ в тот момент, когда ты открываешь файл.
FileNotFoundError не единственное, что может пойти не так. Путь, который оказался папкой, вызывает IsADirectoryError (PermissionError в Windows), а файл, который нельзя прочитать, вызывает PermissionError. Все три являются подклассами OSError, поэтому except OSError: ловит сразу все случаи «не получилось открыть». Как выбирать между перехватом одной конкретной ошибки и целого семейства, разобрано на странице про исключения.
Когда простая проверка уместна
Проверять заранее не всегда ошибка. Это нужный инструмент, когда тебе нужен только ответ и ты не собираешься трогать файл в следующей строке:
- выбрать, какое сообщение показать («Найдено 3 сохранённые игры»)
- проверить путь, который ввёл пользователь, перед запуском долгой задачи
- пропустить уже сделанную работу, когда устаревший ответ стоит лишь повторного запуска
Простое правило: если ты собираешься открыть, прочитать, записать или удалить файл, делай это внутри try. Если тебе нужно только узнать, isfile() или Path.is_file() вполне подойдут.
Создать файл, только если его ещё нет
Бывает и обратный вопрос: «запиши этот файл, но никогда не перезаписывай существующий». Очевидная версия страдает от той же гонки:
if not os.path.exists("report.txt"):
with open("report.txt", "w") as f: # another process may create it first
f.write("new report\n")
Режим "x" (эксклюзивное создание) делает оба шага сразу. Он создаёт файл или выбрасывает FileExistsError, если имя уже занято:
Вывод:
attempt 1: created report.txt
attempt 2: report.txt already exists, left it alone
Для папок os.makedirs(path, exist_ok=True) и Path(path).mkdir(parents=True, exist_ok=True) создают папку и все недостающие родительские папки и ничего не делают, если она уже есть. Вызывать isdir() заранее не нужно.
Частые ошибки
exists()там, где нужен файл. Папка с тем же именем пройдёт проверку. Используйisfile()илиPath.is_file().- Забыть, откуда отсчитываются относительные пути.
"data.txt"ищется в текущей рабочей директории, то есть там, откуда ты запустил программу, а это не обязательно папка со скриптом. Чтобы найти файл рядом со скриптом, собери путь отPath(__file__).parent. - Сначала проверить, потом открыть. Между этими шагами файл может измениться. Открывай внутри
tryи обрабатывайFileNotFoundError. - Перехватывать любые исключения.
except Exception:вокругopen()скрывает ещё и опечатки и баги в коде, который читает файл. ПерехватывайFileNotFoundErrorилиOSError.
Часто задаваемые вопросы
Как проверить в Python, существует ли файл?
Импортируй os и вызови os.path.isfile("data.txt"): он возвращает True только для существующего обычного файла. С pathlib это Path("data.txt").is_file(). Используй os.path.exists() или Path.exists(), если папка с таким именем тоже должна подходить.
В чём разница между os.path.exists и os.path.isfile?
os.path.exists(p) равен True для всего, что есть по этому пути: файла, папки или символической ссылки, которая указывает на что-то существующее. os.path.isfile(p) равен True только для обычного файла, поэтому для папки он возвращает False. Для папок есть парная функция os.path.isdir(p).
Нужно ли проверять, существует ли файл, перед тем как его открыть?
Обычно нет. Между проверкой и вызовом open() файл могут удалить или создать, так что проверка ничего не гарантирует. Открой файл сразу внутри try и обработай FileNotFoundError в except. Отдельная проверка уместна, когда тебе нужен только ответ, например чтобы решить, какое сообщение показать.
Как создать файл, только если его ещё нет?
Открой его в режиме "x": open("report.txt", "x"). Python создаст файл или выбросит FileExistsError, если по этому пути уже что-то есть. Проверка и создание происходят за один шаг, поэтому никакая другая программа не успеет вклиниться между ними.