Python | ファイル・OS 操作:pathlib の結合

Python Python
スポンサーリンク

概要(pathlib の結合は「/ 演算子」で直感的に安全に書く)

pathlib はファイルパスをオブジェクトとして扱う標準ライブラリです。結合は os.path.join を忘れて「/ 演算子」か joinpath を使うのが基本。OS差異(区切り文字)を気にせず、読みやすく安全に書けます。重要なのは「結合のルール(絶対パスが来たら上書き)」「相対/絶対の扱い」「存在確認と作成の分離」を押さえることです。

基本の結合(ここが重要)

/ 演算子でつなぐ(最短で読みやすい)

from pathlib import Path

base = Path("data")
p = base / "logs" / "2025" / "access.log"
print(p)  # data/logs/2025/access.log(OSに応じて自動調整)
Python

「/」は path 結合としてオーバーロードされており、文字列連結より安全で明快です。左が Path なら右は文字列でも Path でもOKです。

joinpath でまとめて渡す(可変個引数)

base = Path("data")
p = base.joinpath("logs", "2025", "access.log")
Python

多段結合を1行で渡したいときに便利です。/ と好みで使い分けて構いません。

Path(…) に複数パーツを渡す(基点から一気に作る)

p = Path("data", "logs", "2025", "access.log")
Python

「最初の引数が絶対パスならそれが基点」になります。相対ならカレントからの相対パスです。

結合ルールの深掘り(上書き・正規化・相対と絶対)

途中で「絶対パス」を渡すとそこから上書きになる

base = Path("data")
p = base / "/var" / "tmp"   # Linux/macOS例
print(p)  # /var/tmp(絶対パスに切り替わる)
Python

pathlib は「絶対パスが現れたら、前の部分を捨ててその絶対パスを基点」にします。意図しない上書きを防ぐには、ユーザー入力などを結合する前にチェックしましょう。

..(親ディレクトリ)や . の扱い(正規化は明示)

p = Path("data") / "logs" / ".." / "output.txt"
print(p)          # data/logs/../output.txt(そのまま保持)
print(p.resolve())# 絶対+正規化(実在パス前提)
print(p.absolute()) # 絶対化(正規化はしない場合あり)
Python

見た目を整えたいなら resolve(実在が前提)か、PurePath系のメソッドを使います。存在しないパスでも正規化したい場合は注意が必要です。

parent・name・stem・suffix で安全に操作

p = Path("data/logs/access.log")
print(p.parent)  # data/logs
print(p.name)    # access.log
print(p.stem)    # access
print(p.suffix)  # .log
Python

結合だけでなく、分解や差し替えを組み合わせると安全なパス操作が書けます。

with_name / with_suffix で差し替え

p = Path("data/logs/access.log")
print(p.with_name("error.log"))   # data/logs/error.log
print(p.with_suffix(".txt"))      # data/logs/access.txt
Python

よくある実務パターン(存在確認・作成・展開)

ディレクトリがなければ作る(parents=True, exist_ok=True)

p = Path("data/logs/2025/access.log")
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("hello\n", encoding="utf-8")
Python

結合→親ディレクトリ作成→書き込みの流れが定番。存在確認は exists() / is_file() / is_dir() を使います。

ホーム・カレントの扱い(~ と cwd)

home = Path("~").expanduser()       # ユーザホーム
p = home / "Downloads" / "report.pdf"

cwd = Path.cwd()                     # カレントディレクトリ
tmp = cwd / "tmp" / "work"
Python

~ は expanduser() で展開します。カレント基準で相対を作るなら Path.cwd() を積極的に使いましょう。

glob で結合先を走査(パターン検索)

base = Path("data/logs/2025")
for f in base.glob("*.log"):
    print(f)            # data/logs/2025/xxx.log
Python

結合した基点からパターン検索。再帰なら rglob(“*/.log”) です。

open と read/write は Path から直接

p = Path("data/logs/access.log")
with p.open("a", encoding="utf-8") as fp:
    fp.write("more\n")

txt = p.read_text(encoding="utf-8")
p.write_bytes(b"\x00\x01")
Python

安全性と設計の勘所(入力の検証・型の一貫性・文字列化)

ユーザー入力と結合するときのガード

  • 絶対パス/ルートの持ち込み(/ やドライブレター)で基点が上書きされます。許可するかを決め、必要なら拒否・正規化しましょう。
  • 「..」での親参照(ディレクトリ・トラバーサル)も検証してから結合します。
def safe_join(base: Path, *parts: str) -> Path:
    p = base.joinpath(*parts).resolve()
    if base.resolve() not in p.parents and p != base.resolve():
        raise ValueError("base外へのパスは禁止")
    return p
Python

Path を最後まで維持して、必要時だけ str にする

p = Path("data") / "logs" / "access.log"
# ライブラリが文字列パスを要求するなら str(p)
some_api(str(p))
Python

中間で文字列化すると OS非依存の利点やメソッド連鎖が失われます。最後のインタフェースだけ変換します。

f-string は表示用途に限定

print(f"Path: {p}")   # OK(表示)
# 結合は f-string ではなく / を使う
Python

文字列連結でパスを作らない。pathlib の演算子とメソッドを使う方が安全です。

例題で身につける(定番から一歩先まで)

例題1:ログファイルの今日分パスを作る

from pathlib import Path
from datetime import date

base = Path("logs")
today = date.today().strftime("%Y%m%d")
p = base / today / "app.log"
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("start\n", encoding="utf-8")
print(p)
Python

例題2:絶対パスの混入を防いで結合

from pathlib import Path

def join_strict(base: Path, *parts: str) -> Path:
    for s in parts:
        if Path(s).is_absolute():
            raise ValueError(f"絶対パスは禁止: {s}")
    return base.joinpath(*parts)

root = Path("/srv/app")
print(join_strict(root, "data", "files", "a.txt"))  # /srv/app/data/files/a.txt
Python

例題3:拡張子を差し替えて出力先作成

from pathlib import Path

src = Path("data/input/report.csv")
out = src.with_suffix(".parquet")
out.parent.mkdir(parents=True, exist_ok=True)
print(out)  # data/input/report.parquet
Python

例題4:ホーム配下に安全に保存(~ 展開)

from pathlib import Path

home = Path("~").expanduser()
save = home / "Documents" / "notes" / "memo.txt"
save.parent.mkdir(parents=True, exist_ok=True)
save.write_text("memo\n", encoding="utf-8")
Python

まとめ

pathlib の結合は「/ 演算子」か joinpath を使うのが最短で安全。絶対パスが混ざると基点が上書きされるルール、resolve/absolute の違い、親ディレクトリ操作(parent)と分解(name/stem/suffix)、差し替え(with_*)まで押さえると迷いません。ディレクトリ作成は mkdir(parents=True, exist_ok=True) をセットで。Path を保ったまま操作し、最後だけ str へ変換する方針で、OS非依存・読みやすさ・安全性を同時に満たしましょう。

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