pathlib の落とし穴:WindowsPath と PosixPath の互換性

pathlib は便利だが、Windows と Unix 系で異なる Path クラスが使われるため、クロスプラットフォーム開発では注意が必要だ。

WindowsPath と PosixPath

pathlib.Path は実行環境に応じて異なるクラスを返す。

from pathlib import Path

p = Path('.')
print(type(p))
# Windows: <class 'pathlib.WindowsPath'>
# Linux/Mac: <class 'pathlib.PosixPath'>

これ自体は便利な機能だが、問題が起きるケースもある。

pickle での互換性問題

Path オブジェクトを pickle でシリアライズすると、異なる OS 間でデシリアライズできない。

import pickle
from pathlib import Path

# Windows で作成
path = Path('C:/Users/data.txt')
with open('path.pkl', 'wb') as f:
    pickle.dump(path, f)

# Linux で読み込むとエラー
with open('path.pkl', 'rb') as f:
    path = pickle.load(f)  # NotImplementedError

対策として、パスを文字列として保存する。

# ✅ 文字列として保存
with open('path.pkl', 'wb') as f:
    pickle.dump(str(path), f)

# 読み込み時に Path に変換
with open('path.pkl', 'rb') as f:
    path = Path(pickle.load(f))

PurePath を使う

実際のファイルシステムにアクセスせずにパス操作だけを行う場合は、PurePath とそのサブクラスを使う。

from pathlib import PurePosixPath, PureWindowsPath

# どの OS でも Windows パスを扱える
win_path = PureWindowsPath('C:/Users/data.txt')
print(win_path.parts)  # ('C:\\', 'Users', 'data.txt')

# どの OS でも Unix パスを扱える
posix_path = PurePosixPath('/home/user/data.txt')
print(posix_path.parts)  # ('/', 'home', 'user', 'data.txt')

PurePath はファイルシステムにアクセスする exists()read_text() などのメソッドを持たない。

パス区切り文字の違い

Windows はバックスラッシュ \、Unix 系はスラッシュ / を使う。

from pathlib import Path

# Windows
path = Path('dir') / 'file.txt'
print(path)  # dir\file.txt

# Linux/Mac
path = Path('dir') / 'file.txt'
print(path)  # dir/file.txt

Path.as_posix() で常にスラッシュ区切りの文字列を取得できる。

from pathlib import Path

path = Path('dir') / 'subdir' / 'file.txt'
print(path.as_posix())  # dir/subdir/file.txt(どの OS でも)

ドライブレターの有無

Windows にはドライブレターがあるが、Unix 系にはない。

from pathlib import PureWindowsPath, PurePosixPath

win = PureWindowsPath('C:/Users/data.txt')
print(win.drive)  # C:
print(win.root)   # /

posix = PurePosixPath('/home/user/data.txt')
print(posix.drive)  # ''(空文字)
print(posix.root)   # /

大文字小文字の扱い

Windows のファイルシステムは大文字小文字を区別しないが、Unix 系は区別する。

from pathlib import Path

# Windows では同じファイル
Path('Data.txt') == Path('data.txt')  # False(Path の比較は文字列比較)

# しかし実際のファイルは同じ
Path('Data.txt').resolve() == Path('data.txt').resolve()  # Windows では True

pathlib の比較は単純な文字列比較なので、ファイルシステムの挙動とは異なる場合がある。

クロスプラットフォームのベストプラクティス

パスを保存・送信する際は文字列に変換する
スラッシュ / を使う(Windows でも / は解釈される)
設定ファイルではスラッシュを使い、読み込み時に Path() で変換する
ドライブレターを前提としたコードを避ける
大文字小文字に依存したロジックを書かない
# ✅ クロスプラットフォーム対応の設定
import json
from pathlib import Path

config = {
    'data_dir': 'data/processed',  # スラッシュを使用
    'output_file': 'results/output.csv'
}

# 読み込み時に Path に変換
data_dir = Path(config['data_dir'])
output_file = Path(config['output_file'])