Krótka odpowiedź
Aby sprawdzić, czy plik istnieje, użyj os.path.isfile() z biblioteki standardowej. Zwraca True, gdy ścieżka wskazuje istniejący zwykły plik, a w przeciwnym razie False. Nigdy nie zgłasza błędu dla brakującej ścieżki.
Ścieżki są względne wobec folderu, w którym uruchamia się program (bieżącego katalogu roboczego), chyba że podasz ścieżkę bezwzględną, na przykład /home/ada/notes.txt albo C:\Users\Ada\notes.txt.
Dalsza część strony opisuje inne sposoby zadania tego pytania, czym się różnią i sytuację, w której w ogóle nie warto go zadawać.
os.path.exists vs isfile vs isdir
Moduł os.path ma trzy pokrewne sprawdzenia, które odpowiadają na nieco inne pytania:
| Funkcja | Zwraca True, gdy ścieżka to |
|---|---|
os.path.exists(p) | cokolwiek, co istnieje: plik, katalog albo działające dowiązanie symboliczne |
os.path.isfile(p) | istniejący zwykły plik |
os.path.isdir(p) | istniejący katalog |
Różnica wychodzi, gdy tylko w grę wchodzi katalog:
Wynik:
reports True False True
reports/summary.txt True True False
reports/old.txt False False False
os.path.exists("reports") daje True, mimo że reports to folder. Jeśli twój kod ma zaraz odczytać ścieżkę jako plik, exists to złe sprawdzenie: katalog by je przeszedł, a potem open("reports") zakończyłoby się błędem (IsADirectoryError na Linuksie i macOS, PermissionError na Windowsie). Wybierz isfile, gdy chodzi ci o plik, i isdir, gdy chodzi o folder.
Jeszcze dwa szczegóły dotyczące tych trzech funkcji os.path:
- Dowiązania symboliczne są rozwijane. Dowiązanie wskazujące prawdziwy plik liczy się jako plik, a zerwane dowiązanie (jego cel zniknął) daje
Falsewe wszystkich trzech. - Jeśli Python nie ma prawa zajrzeć do folderu, sprawdzenie zwraca
Falsezamiast zgłaszaćPermissionError.Falseznaczy "nie znaleziono albo brak dostępu", a nie ściśle "nie istnieje".
Sprawdzanie przez pathlib
pathlib to obiektowe API do ścieżek w bibliotece standardowej i większość nowego kodu z niego korzysta. Obiekt Path ma te same trzy sprawdzenia jako metody:
Path.exists(), Path.is_file() i Path.is_dir() działają jak os.path.exists, isfile i isdir. Operator / łączy części ścieżki właściwym separatorem dla systemu operacyjnego, więc folder / "summary.txt" działa tak samo na Windowsie, macOS i Linuksie. Zajrzyj do strony o obsłudze plików, by czytać i zapisywać przez obiekty Path.
Którego użyć? Jeśli reszta twojego kodu już przekazuje obiekty Path, zostań przy pathlib. Jeśli pracujesz na zwykłych napisach i funkcjach z os, os.path.isfile jest równie poprawne. Dają te same odpowiedzi.
Dlaczego sprawdzanie z góry może być błędem
Najczęstszy powód, by sprawdzić, czy plik istnieje, to otwarcie go zaraz potem:
import os
if os.path.isfile("config.txt"):
with open("config.txt") as f: # plik mógł już zniknąć
settings = f.read()
else:
settings = ""
Tu jest luka. Między wywołaniem isfile() a wywołaniem open() inny program, inny wątek albo sam użytkownik może usunąć plik lub zmienić jego nazwę. Sprawdzenie dało True, otwarcie i tak się nie udaje, a program pada z FileNotFoundError. Taka luka to wyścig (race condition), zwany też "time of check to time of use" (od chwili sprawdzenia do chwili użycia). Na własnym laptopie zdarza się rzadko, a dużo częściej na obciążonym serwerze, gdzie kilka procesów dzieli jeden folder.
Rozwiązanie: pomiń osobne sprawdzenie i niech open() będzie sprawdzeniem. Python zgłasza FileNotFoundError, gdy pliku brakuje, a ty obsługujesz to w except:
Wynik:
theme=dark
(no settings file, using defaults)
Teraz jest jedna operacja zamiast dwóch, więc nie ma się z czym ścigać. Ten styl ma w społeczności Pythona swoją nazwę: EAFP, "easier to ask forgiveness than permission" (łatwiej prosić o wybaczenie niż o pozwolenie). Styl przeciwny, czyli sprawdzanie z góry, to LBYL, "look before you leap" (rozejrzyj się, zanim skoczysz). Przy pracy z plikami Python skłania się ku EAFP, bo tylko system operacyjny zna odpowiedź w chwili otwierania pliku.
FileNotFoundError to nie jedyne, co może pójść źle. Ścieżka będąca katalogiem zgłasza IsADirectoryError (PermissionError na Windowsie), a plik, którego nie możesz odczytać, zgłasza PermissionError. Wszystkie trzy są podklasami OSError, więc except OSError: łapie naraz każdy przypadek "nie udało się otworzyć". Strona o wyjątkach wyjaśnia, jak wybrać między łapaniem jednego konkretnego błędu a łapaniem całej rodziny.
Kiedy zwykłe sprawdzenie wystarcza
Sprawdzanie z góry nie zawsze jest złe. To dobre narzędzie, gdy potrzebujesz tylko odpowiedzi i nie zamierzasz w następnej linii nic robić z plikiem:
- wybór komunikatu do pokazania ("Found 3 saved games")
- sprawdzenie ścieżki wpisanej przez użytkownika przed rozpoczęciem długiego zadania
- pominięcie pracy, która już została wykonana, gdy nieaktualna odpowiedź kosztuje tylko powtórkę
Zasada: jeśli masz otworzyć, odczytać, zapisać albo usunąć plik, zrób to w try. Jeśli chcesz tylko wiedzieć, isfile() albo Path.is_file() w zupełności wystarczy.
Tworzenie pliku tylko wtedy, gdy nie istnieje
Pojawia się też odwrotne pytanie: "zapisz ten plik, ale nigdy nie nadpisuj istniejącego". Oczywista wersja ma ten sam wyścig co wcześniej:
if not os.path.exists("report.txt"):
with open("report.txt", "w") as f: # inny proces może go utworzyć pierwszy
f.write("new report\n")
Tryb "x" (wyłączne tworzenie) robi oba kroki naraz. Tworzy plik albo zgłasza FileExistsError, jeśli nazwa jest zajęta:
Wynik:
attempt 1: created report.txt
attempt 2: report.txt already exists, left it alone
Dla folderów os.makedirs(path, exist_ok=True) i Path(path).mkdir(parents=True, exist_ok=True) tworzą katalog i wszystkie brakujące katalogi nadrzędne, a nic nie robią, jeśli już istnieje. Nie trzeba wcześniej wywoływać isdir().
Częste błędy
- Używanie
exists(), gdy chodzi o plik. Katalog o tej samej nazwie przejdzie sprawdzenie. Użyjisfile()alboPath.is_file(). - Zapominanie, skąd zaczynają się ścieżki względne.
"data.txt"jest szukany w bieżącym katalogu roboczym, czyli tam, skąd uruchomiono program, a niekoniecznie w folderze ze skryptem. Aby znaleźć plik obok skryptu, zbuduj ścieżkę odPath(__file__).parent. - Sprawdzanie, a potem otwieranie. Plik może się w międzyczasie zmienić. Otwieraj w
tryi obsługujFileNotFoundError. - Łapanie każdego wyjątku.
except Exception:wokółopen()ukrywa też literówki i błędy w kodzie, który czyta plik. ŁapFileNotFoundErroralboOSError.
Najczęściej zadawane pytania
Jak sprawdzić, czy plik istnieje w Pythonie?
Zaimportuj os i wywołaj os.path.isfile("data.txt"), które zwraca True tylko dla istniejącego zwykłego pliku. Z pathlib to Path("data.txt").is_file(). Użyj os.path.exists() albo Path.exists(), gdy katalog o tej nazwie też ma się liczyć.
Czym różni się os.path.exists od os.path.isfile?
os.path.exists(p) daje True dla wszystkiego pod tą ścieżką: pliku, katalogu albo dowiązania symbolicznego, które wskazuje na coś istniejącego. os.path.isfile(p) daje True tylko dla zwykłego pliku, więc dla katalogu zwraca False. os.path.isdir(p) to odpowiednik dla katalogów.
Czy trzeba sprawdzać, czy plik istnieje, zanim go otworzysz?
Zwykle nie. Plik może zostać usunięty albo utworzony między sprawdzeniem a wywołaniem open(), więc sprawdzenie niczego nie gwarantuje. Otwórz plik od razu w try i obsłuż FileNotFoundError w except. Osobne sprawdzenie ma sens, gdy potrzebujesz tylko odpowiedzi, na przykład by zdecydować, jaki komunikat pokazać.
Jak utworzyć plik tylko wtedy, gdy nie istnieje?
Otwórz go w trybie "x": open("report.txt", "x"). Python tworzy plik albo zgłasza FileExistsError, jeśli coś już tam jest. Sprawdzenie i utworzenie dzieją się w jednym kroku, więc żaden inny program nie wciśnie się między nie.