【Python】pathlibでファイルパスをモダンに扱う

Python

こんにちは、かつコーチです。

Pythonでファイルパスを扱うとき、os.pathモジュールを使っている方は多いと思います。
ただ、Python 3.4以降では、より直感的に書けるpathlibという標準ライブラリが用意されています。

pathlibは、ファイルパスをPathオブジェクトという1つのオブジェクトとして扱う仕組みです。
文字列としてパスを操作していたos.pathと違い、パスに対してメソッドを呼び出す形で処理を書けます。

この記事では、pathlibの基本的な使い方と、os.pathとの違いを解説します。
中級者の方が、より読みやすいコードを書けるようになることを目指します。

pathlibの基本操作

os.pathとpathlibの違い

まずは同じ処理をos.pathpathlibの両方で書いて比較してみましょう。

# os.pathの場合
import os

base_dir = "data"
file_name = "report.csv"
file_path = os.path.join(base_dir, file_name)

print(os.path.exists(file_path))
print(os.path.splitext(file_path)[1])
# pathlibの場合
from pathlib import Path

base_dir = Path("data")
file_path = base_dir / "report.csv"

print(file_path.exists())
print(file_path.suffix)

pathlibでは/演算子を使ってパスを結合できます。
文字列連結ではなくPythonの演算子として自然に書けるため、コードの見通しがよくなります。

Pathオブジェクトの基本操作

Pathオブジェクトには、パスに関する情報を取得する便利なプロパティが揃っています。

from pathlib import Path

file_path = Path("data/report.csv")

print(file_path.name)      # report.csv(ファイル名)
print(file_path.stem)      # report(拡張子なしのファイル名)
print(file_path.suffix)    # .csv(拡張子)
print(file_path.parent)    # data(親ディレクトリ)
print(file_path.exists())  # ファイルの存在確認

os.pathではos.path.splitext()os.path.dirname()など、目的ごとに別の関数を呼び出す必要がありました。
pathlibではこれらがすべてPathオブジェクトのプロパティとして統一されており、覚えることが少なくて済みます。

ディレクトリの作成とファイルの読み書き

ディレクトリの作成もPathオブジェクトのメソッドで完結します。

from pathlib import Path

output_dir = Path("output/2026/08")
output_dir.mkdir(parents=True, exist_ok=True)

parents=Trueは、途中の親ディレクトリがなければまとめて作成するという意味です。
os.makedirs()と似ていますが、こちらもメソッド呼び出しの形で統一されています。

ファイルの読み書きも、open()を使わずに直接メソッドで行えます。

from pathlib import Path

file_path = Path("output/2026/08/note.txt")

# 書き込み
file_path.write_text("こんにちは、かつコーチです。", encoding="utf-8")

# 読み込み
content = file_path.read_text(encoding="utf-8")
print(content)

短いテキストファイルの読み書きであれば、write_text()read_text()だけで完結します。
with open(...) as f:という定型文を書かずに済むのは、pathlibの大きな利点です。

glob()でファイルを検索する

指定したパターンに合うファイルを一括で探したいときはglob()を使います。

from pathlib import Path

data_dir = Path("data")

# dataディレクトリ直下のcsvファイルを検索
csv_files = list(data_dir.glob("*.csv"))
print(csv_files)
# [PosixPath('data/report.csv'), PosixPath('data/sales.csv')]

# サブディレクトリも含めて再帰的に検索
all_csv_files = list(data_dir.glob("**/*.csv"))
print(all_csv_files)

*.csvは「拡張子がcsvのすべてのファイル」を意味するパターンです。
**/*.csvのように**を使うと、サブディレクトリの中まで再帰的に検索できます。

よくあるつまずきポイント・エラー対処

Pathオブジェクトと文字列を混同してエラーになる

Before(つまずいたコード)

from pathlib import Path

file_path = Path("data/report.csv")

if file_path == "data/report.csv":
    print("一致しました")
else:
    print("一致しません")

私は最初、Pathオブジェクトと文字列を単純に==で比較して、思った通りの結果にならず戸惑いました。
実行結果は次のようになります。

一致しません

Path("data/report.csv")はPathオブジェクトであり、文字列の"data/report.csv"とは型が異なります。
Python 3.12系では、環境によってはこの比較がTrueになる場合もありますが、原則として型の違いを意識しないと予期しない挙動につながります。

After(改善したコード)

from pathlib import Path

file_path = Path("data/report.csv")

if str(file_path) == "data/report.csv":
    print("一致しました")

比較したい場合はstr()で文字列に変換してから比較するのが確実です。
逆に、外部ライブラリが文字列のパスを要求する場合も、str(file_path)で変換してから渡す必要があります。

globの結果がジェネレータで扱いに戸惑う

Before(つまずいたコード)

from pathlib import Path

data_dir = Path("data")
csv_files = data_dir.glob("*.csv")

print(len(csv_files))

このコードを実行すると、次のエラーが発生します。

TypeError: object of type 'generator' has no len()

After(改善したコード)

from pathlib import Path

data_dir = Path("data")
csv_files = list(data_dir.glob("*.csv"))

print(len(csv_files))
# 2

glob()の戻り値はジェネレータ(値を1つずつ順番に生成するイテレータ)であり、リストではありません。
件数を数えたり、複数回ループしたりしたい場合は、list()で明示的にリスト化してから扱いましょう。

まとめ

この記事のポイント

  • pathlibはファイルパスをPathオブジェクトとして扱う標準ライブラリです
  • /演算子でパスを結合でき、os.pathより直感的に書けます
  • .name.stem.suffix.parentなどのプロパティで情報を取得できます
  • write_text()read_text()open()を使わずファイル読み書きができます
  • glob()はジェネレータを返すため、必要に応じてlist()で変換しましょう

次に読むべき記事

os.pathベースの操作を先に押さえたい方は、osモジュールの基本記事もあわせてご覧ください。
正規表現でファイル名を柔軟に検索したい方は、reモジュールの解説記事も参考になります。

タグ: Python, 中級者向け, 標準ライブラリ

タイトルとURLをコピーしました