閉じる

【Python】pathlib入門 Pathでパス結合/走査/存在確認をやさしく解説

ファイルやフォルダのパス操作は、どのPythonプロジェクトでも避けて通れません。

pathlibは、OS差を意識せず直感的にパスを扱える標準ライブラリです。

本記事ではPathの使い方を、パスの結合、ディレクトリ走査、存在確認、基本的なファイル操作まで順にやさしく解説します。

Python初心者の方でも今日から実務で使えるようになることを目指します。

pathlibとは?Pythonでモダンなパス操作

Pathの基本

pathlibはPython標準ライブラリで、パス文字列を「文字列」ではなく「オブジェクト」として扱います。

中心となるクラスがPathです。

スラッシュ/演算子で自然に連結でき、パスの各部分に簡単にアクセスできます。

Python
# pathlibの基本: Pathオブジェクトを作り、結合や分解をしてみます
from pathlib import Path

# 相対パスをオブジェクトで表現
p = Path("data") / "images" / "logo.png"

print(p)         # OS依存の見た目で表示されます
print(p.parts)   # パスを各要素に分解
print(p.name)    # ファイル名部分
print(p.suffix)  # 拡張子
print(p.stem)    # 拡張子を除いた名前

出力例(POSIX系 macOS/Linux)

実行結果
data/images/logo.png
('data', 'images', 'logo.png')
logo.png
.png
logo

出力例(Windows)

実行結果
data\images\logo.png
('data', 'images', 'logo.png')
logo.png
.png
logo

カレント/ホームディレクトリへアクセス

今いるディレクトリやホームディレクトリには、メソッド1つでアクセスできます。

Python
from pathlib import Path

cwd = Path.cwd()     # カレントディレクトリ
home = Path.home()   # ホームディレクトリ

print("cwd:", cwd)
print("home:", home)
実行結果
cwd: /Users/alice/projects/myapp
home: /Users/alice

OS差を意識しないパス表現

PathはOSに合わせて区切り文字やドライブ表現を自動調整します。

コード上では/演算子で結合すればOKです。

Python
from pathlib import Path

# OS非依存に結合できる
conf = Path("config") / "app.ini"
print(conf)  # Windowsなら \ 区切り、Linux/Macなら / 区切りで表示
実行結果
config/app.ini  # macOS/Linux
# または
config\app.ini  # Windows
注意

Windowsの手打ちパスで\n\tがエスケープとして解釈されることがあります。

文字列リテラルにWindowsパスを書くなら、次のいずれかを使います。

Python
from pathlib import Path

# NG: \n が改行として解釈される
bad = Path("C:\new\notes.txt")

# OK: raw文字列を使う
good1 = Path(r"C:\new\notes.txt")

# OK: スラッシュに統一してもWindowsで正しく解釈される
good2 = Path("C:/new/notes.txt")

print(good1)
print(good2)

Pathとos.pathの違い

Pathはオブジェクト指向・直感的、os.pathは関数指向・文字列中心です。

どちらも標準ですが、これから新規で書くならpathlibが推奨です。

多くの標準関数はPathをそのまま受け取れるため、相互運用も問題ありません。

次の表は、よく使う操作の対応関係です。

やりたいことos.path系pathlib系
パス結合os.path.join(a, b)Path(a) / b
親ディレクトリos.path.dirname(p)Path(p).parent
ベース名os.path.basename(p)Path(p).name
拡張子os.path.splitext(p)[1]Path(p).suffix / suffixes
拡張子除いた名os.path.splitext(p)[0]Path(p).stem
存在確認os.path.exists(p)Path(p).exists()
ディレクトリ作成os.makedirs(p, exist_ok=True)Path(p).mkdir(parents=True, exist_ok=True)
中身の列挙os.listdir(p)Path(p).iterdir()
ワイルドカードglob.glob(pattern)Path(p).glob(pattern) / rglob(pattern)
ファイルを開くopen(p, mode=…)Path(p).open(mode=…)

相互運用の例です。

多くの組み込み関数はPathオブジェクトをそのまま受け取れます

Python
from pathlib import Path
import json

path = Path("settings.json")

# Path.open で書く
with path.open("w", encoding="utf-8") as f:
    json.dump({"debug": True}, f, ensure_ascii=False, indent=2)

# 組み込み open に Path を渡してもOK
with open(path, "r", encoding="utf-8") as f:
    data = json.load(f)

print(data)
実行結果
{'debug': True}

Pathでパス結合の基本

/演算子で直感的にパス結合

スラッシュ/演算子でパスをつなげます。

可読性が高く、引数順を間違えにくいのが利点です。

Python
from pathlib import Path

root = Path("project")
p1 = root / "src" / "main.py"
p2 = root / "README.md"

print(p1)
print(p2)
実行結果
project/src/main.py
project/README.md
補足

右側が絶対パスの場合、左側は破棄されます。

これはOS共通の仕様です。

Python
from pathlib import Path

print(Path("/etc") / "hosts")       # => /etc/hosts
print(Path("/etc") / Path("/var"))  # => /var  (右が絶対パスなので左は捨てられる)
実行結果
/etc/hosts
/var

相対パスと絶対パス

相対パスは現在のカレントディレクトリ基準で解釈され、絶対パスはドライブやルートから始まります。

Python
from pathlib import Path

rel = Path("logs/app.log")
abs1 = Path("/var/log/system.log")  # POSIXの例
print("rel is absolute?", rel.is_absolute())
print("abs1 is absolute?", abs1.is_absolute())

# 実体の有無にかかわらず、絶対パスへ解決(シンボリックリンクの解決は環境次第)
print("resolved rel:", rel.resolve())
実行結果
rel is absolute? False
abs1 is absolute? True
resolved rel: /Users/alice/projects/myapp/logs/app.log
注意

resolve()は既定ではstrict=Falseです。

存在しないパスも「計算上の絶対パス」に展開します。

実在チェックはexists()で行います。

name/suffix/stemでファイル名と拡張子を取得

ファイル名や拡張子の取得・変更がメソッドやプロパティで簡単にできます。

Python
from pathlib import Path

p = Path("/path/to/archive.tar.gz")

print("name:", p.name)          # archive.tar.gz
print("suffix:", p.suffix)      # .gz
print("suffixes:", p.suffixes)  # ['.tar', '.gz']
print("stem:", p.stem)          # archive.tar

# 拡張子の置き換え
print("zip:", p.with_suffix(".zip"))
実行結果
name: archive.tar.gz
suffix: .gz
suffixes: ['.tar', '.gz']
stem: archive.tar
zip: /path/to/archive.tar.zip

parent/parentsで親ディレクトリをたどる

parentで1つ上、parentsで上位を順に取得します。

Python
from pathlib import Path

p = Path("/usr/local/bin/python3")

print("parent:", p.parent)  # /usr/local/bin

# 祖先ディレクトリを上から順に
for i, anc in enumerate(p.parents, start=1):
    print(f"{i}: {anc}")
実行結果
parent: /usr/local/bin
1: /usr/local
2: /usr
3: /

ディレクトリ走査と検索

iterdirでフォルダ内を走査

iterdir()で直下のエントリを列挙できます。

種類で分岐するのも簡単です。

Python
from pathlib import Path

base = Path.cwd()  # 今いるディレクトリ
for p in sorted(base.iterdir()):
    kind = "DIR " if p.is_dir() else ("FILE" if p.is_file() else "OTHER")
    print(kind, p.name)

出力例(あなたの環境で異なります):

text
DIR  .git
FILE README.md
DIR  src
FILE pyproject.toml
OTHER .DS_Store

globで拡張子やワイルドカード検索

glob()はワイルドカード検索ができます。

直下のみが対象です。

Python
from pathlib import Path

for p in Path.cwd().glob("*.py"):
    print(p.name)
実行結果
main.py
utils.py
test_main.py

パターン例は*.txtdata_??.csv[ab]*.mdなどが使えます。

rglobで再帰的に走査

サブディレクトリも含めて検索したいときはrglob()を使います。

Python
from pathlib import Path

# プロジェクト内の全ての .py を再帰的に列挙
for p in Path.cwd().rglob("*.py"):
    print(p)
実行結果
/Users/alice/projects/myapp/main.py
/Users/alice/projects/myapp/src/utils/io.py
/Users/alice/projects/myapp/tests/test_main.py

存在確認と基本のファイル操作

pathlibだけで作成・読み書き・移動・削除まで一通りの操作が完結します。

以下の例では、tempfile.TemporaryDirectoryを使って一時ディレクトリ内で安全に操作します。

exists/is_file/is_dirで存在と種類を確認

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    base = Path(tmp)
    f = base / "hello.txt"
    d = base / "docs"

    print("初期:", f.exists(), f.is_file(), d.is_dir())

    # 作成
    d.mkdir()
    f.write_text("hello", encoding="utf-8")

    print("作成後:", f.exists(), f.is_file(), d.is_dir())
実行結果
初期: False False False
作成後: True True True

mkdirでディレクトリ作成

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    deep = Path(tmp) / "a" / "b" / "c"
    # 親が無くてもまとめて作る
    deep.mkdir(parents=True, exist_ok=True)
    print("作成できたか:", deep.exists(), "親:", deep.parent)
実行結果
作成できたか: True 親: /tmp/tmpxxxxxx/a/b
ポイント

既に存在していてもエラーにしたくない場合はexist_ok=Trueを付けます。

read_text/write_textでテキストを読み書き

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    f = Path(tmp) / "note.txt"

    # 文字コードは明示するのが安全(UTF-8推奨)
    f.write_text("1行目\n2行目", encoding="utf-8")

    content = f.read_text(encoding="utf-8")
    print(content)
実行結果
1行目
2行目
メモ

バイナリはwrite_bytes()read_bytes()が使えます。

rename/replaceでリネーム

renameは通常の移動・改名、replaceは上書き前提の移動です。

ターゲットが既に存在する場合、rename()は失敗することがありますが、replace()は置き換えます。

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    base = Path(tmp)
    src = base / "a.txt"
    dst = base / "b.txt"
    src.write_text("A", encoding="utf-8")

    # 通常のリネーム
    src.rename(dst)
    print("dst exists after rename:", dst.exists())

    # replace は既存ファイルを上書きする
    tmp2 = base / "tmp.txt"
    tmp2.write_text("NEW", encoding="utf-8")
    dst.write_text("OLD", encoding="utf-8")  # 既にある状態
    tmp2.replace(dst)  # b.txt を NEW で置き換える
    print("dst content:", dst.read_text(encoding="utf-8"))
実行結果
dst exists after rename: True
dst content: NEW

unlinkでファイル削除

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    f = Path(tmp) / "remove_me.txt"
    f.write_text("bye", encoding="utf-8")

    f.unlink()  # 削除
    print("exists after unlink:", f.exists())

    # 無いものを削除してもエラーにしたくない場合
    f.unlink(missing_ok=True)
    print("missing_okでもエラーにならない")
実行結果
exists after unlink: False
missing_okでもエラーにならない
注意

ディレクトリを消すときはrmdir()ですが、空でないと失敗します。

再帰的に消すならshutil.rmtree()を使います。

Path.openでファイルを開く

with文と組み合わせて安全に読み書きできます。

改行コードを制御したいときはnewline=""を指定します。

Python
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    path = Path(tmp) / "data.csv"

    # 書き込み: UTF-8で、改行はそのまま扱う(newline="")
    with path.open("w", encoding="utf-8", newline="") as f:
        f.write("id,name\n")
        f.write("1,Alice\n")
        f.write("2,Bob\n")

    # 読み込み
    with path.open("r", encoding="utf-8") as f:
        lines = [line.strip() for line in f]
    print(lines)
実行結果
['id,name', '1,Alice', '2,Bob']

まとめ

本記事では標準ライブラリpathlibを使ったモダンなパス操作を、基本から一通り紹介しました。

PathはOS差を意識せずスラッシュ演算子で直感的に結合でき、存在確認や走査、読み書きまで一貫したAPIで扱えるのが長所です。

従来のos.pathよりも可読性と保守性が向上し、初心者でも誤りを減らしやすくなります。

まずは日々のスクリプトでPathを使い始め、iterdir()glob()mkdir()read_text()などを実際に動かしてみてください。

「パスはオブジェクトで扱う」という発想に慣れるだけで、ファイル操作が驚くほど快適になります。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!