結論
ファイルが存在するかを確認するには、標準ライブラリの os.path.isfile() を使います。パスが存在する通常ファイルを指していれば True、そうでなければ False を返します。パスが見つからなくてもエラーは発生しません。
絶対パス(/home/ada/notes.txt や C:\Users\Ada\notes.txt など)を渡さない限り、パスはプログラムを実行しているフォルダ(カレントディレクトリ)を基準に解釈されます。
このページでは、存在を確かめる別の方法とその違い、そして確認すべきでないケースを順に説明します。
os.path.exists・isfile・isdir の違い
os.path モジュールには関連する3つの確認関数があり、それぞれ少しずつ違うことを調べます。
| 関数 | 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
reports はフォルダなのに、os.path.exists("reports") は True になります。そのパスをファイルとして読むつもりなら、exists は適切な確認ではありません。ディレクトリでも通過してしまい、その後の open("reports") が失敗します(Linux と macOS では IsADirectoryError、Windows では PermissionError)。ファイルを確認したいなら isfile、フォルダなら isdir を選びましょう。
この3つの os.path 関数について、さらに2つ知っておくべき点があります。
- シンボリックリンクはリンク先までたどられます。実在するファイルを指すリンクはファイルとして扱われ、リンク切れ(リンク先が消えている)の場合は3つとも
Falseを返します。 - フォルダの中を見る権限が Python にない場合、
PermissionErrorを送出せずにFalseを返します。つまりFalseは厳密な「存在しない」ではなく、「見つからない、またはアクセスできない」という意味です。
pathlib で確認する
pathlib は標準ライブラリに含まれるオブジェクト指向のパス API で、新しいコードの多くはこちらを使います。Path オブジェクトは同じ3つの確認をメソッドとして持っています。
Path.exists()、Path.is_file()、Path.is_dir() は、os.path.exists、isfile、isdir と同じように動作します。/ 演算子は OS に合った区切り文字でパスの要素をつなぐので、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 だったのに open は失敗し、結局プログラムは FileNotFoundError で落ちます。この隙間は競合状態(レースコンディション)、または TOCTOU(time of check to time of use)と呼ばれます。自分のノートPCではめったに起きませんが、複数のプロセスが同じフォルダを共有する忙しいサーバーではずっと起きやすくなります。
解決策は、個別の確認をやめて open() そのものに確認させることです。ファイルがなければ Python は FileNotFoundError を送出するので、それを except で処理します。
出力:
theme=dark
(no settings file, using defaults)
操作が2つから1つになったので、競合する余地がありません。このスタイルは Python コミュニティで EAFP(easier to ask forgiveness than permission、「認可をとるより許しを請う方が容易」)と呼ばれています。先に確認する逆のスタイルは LBYL(look before you leap、「転ばぬ先の杖」)です。ファイルを開くその瞬間の答えを知っているのは OS だけなので、ファイル操作では Python は EAFP を好みます。
失敗の原因は FileNotFoundError だけではありません。パスがディレクトリなら IsADirectoryError(Windows では PermissionError)、読み取り権限のないファイルなら PermissionError が発生します。この3つはどれも 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"(排他的作成)なら、2つの手順を1回で行えます。ファイルを作成し、その名前がすでに使われていれば 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を処理しましょう。 - あらゆる例外を捕捉する。
open()をexcept Exception:で囲むと、ファイルを読むコードのタイプミスやバグまで隠れてしまいます。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 の中で直接ファイルを開き、except で FileNotFoundError を処理しましょう。表示するメッセージを決めるときのように、答えだけが必要な場面では個別の存在確認が適しています。
ファイルが存在しない場合だけ作成するには?
モード "x" で開きます: open("report.txt", "x")。Python はファイルを作成し、すでに同じ名前のものがあれば FileExistsError を送出します。確認と作成が1回の操作で行われるので、ほかのプログラムがその間に割り込むことはできません。