Masayan tech blog.

  1. ブログ記事一覧>
  2. 【Python】pathlibでOSに依存しないパス操作を実現する完全ガイド | Windows・Mac対応

【Python】pathlibでOSに依存しないパス操作を実現する完全ガイド | Windows・Mac対応

公開日
最終更新日

はじめに:なぜpathlibが推奨されるのか

PythonでWindowsとMac(Linux)の両方で動作するコードを書く際、ファイルパスの扱いは避けて通れない課題です。WindowsはC:\Users\のようにバックスラッシュを使い、Mac/Linuxは/home/user/のようにスラッシュを使うため、パス文字列を直接扱うとOS依存のコードになってしまいます。

本記事では、Python 3.4以降で標準搭載されたpathlibモジュールを使って、OSに依存しないパス操作を実現する方法を詳しく解説します。

検証環境

  • OS: Windows 10
  • Python: 3.10.1
  • エディタ: VSCode

Pythonでパスを扱う3つの方法

Pythonには、ディレクトリ・ファイルパスを扱うモジュールが3種類存在します。

モジュール

特徴

推奨度

os.path

古典的な文字列ベースのパス操作

glob

パターンマッチングに特化

pathlib

オブジェクト指向でモダンなパス操作

pathlibが推奨される理由:

  • オブジェクト指向で直感的な操作が可能
  • OS間の差異を自動的に吸収
  • Python 3.4以降で標準搭載(外部ライブラリ不要)
  • メソッドチェーンで可読性の高いコードが書ける

パスの基礎知識:絶対パスと相対パスの違い

絶対パス(Absolute Path)

ルートディレクトリから始まる完全なパス

  • Windows: C:\Users\username\Documents\file.txt
  • Mac/Linux: /home/username/documents/file.txt

相対パス(Relative Path)

現在の作業ディレクトリ(カレントディレクトリ)を起点としたパス

  • 例: ./documents/file.txt または documents/file.txt
  • .. で親ディレクトリを指定可能

pathlibの基本的な使い方

1. Pathオブジェクトの生成

import pathlib

# パス文字列からPathオブジェクトを生成
path = pathlib.Path("/var/www/html")
print(path)  # WindowsではWindowsPath('\\var\\www\\html')
             # MacではPosixPath('/var/www/html')

ポイント:

  • WindowsではWindowsPath、Mac/LinuxではPosixPathが自動的に生成される
  • 引数のパス文字列はスラッシュ(/)で記述してOK(OSに依存しない)
  • 絶対パス・相対パスどちらでも指定可能

2. Pathオブジェクトを文字列に変換

path = pathlib.Path("/var/www/html")
path_str = str(path)
print(path_str)  # Windows: '\\var\\www\\html'
                 # Mac: '/var/www/html'

3. 現在の作業ディレクトリを取得

current_dir = pathlib.Path.cwd()
print(current_dir)  # 例: 'C:\\Users\\username\\project'

cwd()の用途:

  • スクリプトの実行場所を基準にしたパス操作
  • 相対パスから絶対パスへの変換の起点

pathlibで実現する実践的なパス操作

パスの存在確認

path = pathlib.Path("sample.txt")

# パスが存在するかチェック
if path.exists():
    print("ファイルが存在します")
else:
    print("ファイルが見つかりません")

絶対パス・相対パスの判定

abs_path = pathlib.Path("/var/www/html")
rel_path = pathlib.Path("documents/file.txt")

print(abs_path.is_absolute())  # True
print(rel_path.is_absolute())  # False

# 注:相対パスかどうかを判定するメソッドは存在しない
# is_absolute()がFalseなら相対パスと判断する

絶対パスと相対パスの相互変換

相対パス → 絶対パス

rel_path = pathlib.Path("documents/file.txt")
abs_path = rel_path.resolve()
print(abs_path)  # 'C:\\Users\\username\\project\\documents\\file.txt'

resolve()の動作:

  • 現在の作業ディレクトリ(cwd)に相対パスを結合
  • シンボリックリンクも解決される

絶対パス → 相対パス

abs_path = pathlib.Path("/var/www/html/index.html")
base_path = pathlib.Path("/var/www")

rel_path = abs_path.relative_to(base_path)
print(rel_path)  # 'html/index.html'

注意点:

  • relative_to()は、指定した基底パスが元のパスに含まれていないとValueErrorが発生
  • 異なるドライブ間では変換不可(Windows)

パスの結合(連結)

base = pathlib.Path("projects")
file_path = base.joinpath("myapp", "main.py")
print(file_path)  # 'projects\\myapp\\main.py'

# スラッシュ演算子(/)でも結合可能(Python 3.4+)
file_path2 = base / "myapp" / "main.py"
print(file_path2)  # 'projects\\myapp\\main.py'

おすすめ: /演算子を使った結合の方が直感的で可読性が高い

ファイルとディレクトリの判定

path = pathlib.Path("sample.txt")

# ファイルかどうか
if path.is_file():
    print("ファイルです")

# ディレクトリかどうか
if path.is_dir():
    print("ディレクトリです")

重要な仕様:

  • 存在しないパスに対しては、is_file()is_dir()Falseを返す
  • 事前にexists()で存在確認するのが安全

ホームディレクトリの取得

home = pathlib.Path.home()
print(home)
# Windows: 'C:\\Users\\username'
# Mac/Linux: '/home/username'

実務で使える:パス操作専用クラスの実装例

上記の基本操作を組み合わせた、実務で使いやすいラッパークラスの例:

import pathlib
from typing import Optional

class XFileSystemPath:
    """OS非依存のファイルシステムパスを扱うクラス"""
    
    def __init__(self, path_str: str):
        """
        Args:
            path_str: パス文字列(絶対パス・相対パスどちらでもOK)
        """
        self._path = pathlib.Path(path_str)

    def path(self) -> pathlib.Path:
        """内部のPathオブジェクトを取得"""
        return self._path

    def of_text(self) -> str:
        """パス文字列として取得"""
        return str(self.path())

    def to_relative(self, base_path: Optional[pathlib.Path] = None):
        """
        絶対パスを相対パスに変換
        
        Args:
            base_path: 基準となるパス(省略時はcwd)
        
        Returns:
            XFileSystemPath: 相対パスのインスタンス
        
        Raises:
            ValueError: 基準パスが絶対パスに含まれていない場合
        """
        if base_path is None:
            base_path = self.path().cwd()

        try:
            relative = self.to_absolute().path().relative_to(base_path)
            return XFileSystemPath(str(relative))
        except ValueError:
            raise ValueError(
                f"基準パス '{base_path}' は絶対パス '{self.of_text()}' に含まれていません"
            )

    def to_absolute(self):
        """相対パスを絶対パスに変換"""
        return XFileSystemPath(str(self.path().resolve()))

    @staticmethod
    def cwd():
        """現在の作業ディレクトリを取得"""
        return XFileSystemPath(str(pathlib.Path.cwd()))

    @staticmethod
    def home_dir():
        """ホームディレクトリを取得"""
        return XFileSystemPath(str(pathlib.Path.home()))

    def exists(self) -> bool:
        """パスが存在するか確認"""
        return self.path().exists()

    def is_absolute(self) -> bool:
        """絶対パスかどうか判定"""
        return self.path().is_absolute()

    def is_file(self) -> bool:
        """
        ファイルかどうか判定
        注: 存在しないパスは常にFalse
        """
        return self.path().is_file()

    def is_dir(self) -> bool:
        """
        ディレクトリかどうか判定
        注: 存在しないパスは常にFalse
        """
        return self.path().is_dir()

    def join(self, *more_path_strs: str):
        """
        パスを結合
        
        Args:
            *more_path_strs: 結合するパス文字列(可変長引数)
        
        Returns:
            XFileSystemPath: 結合後のパス
        """
        joined = str(self.path().joinpath(*more_path_strs))
        return XFileSystemPath(joined)

このクラスの利点:

  • 型ヒントによる補完が効く
  • エラーメッセージが分かりやすい
  • メソッド名が統一されている
  • 可変長引数で柔軟なパス結合が可能

os.pathとの比較:pathlibを選ぶべき理由

操作

os.path

pathlib

パス結合

os.path.join(a, b)

pathlib.Path(a) / b

絶対パス取得

os.path.abspath(p)

pathlib.Path(p).resolve()

存在確認

os.path.exists(p)

pathlib.Path(p).exists()

ファイル判定

os.path.isfile(p)

pathlib.Path(p).is_file()

可読性

文字列操作で分かりにくい

オブジェクト指向で直感的

pathlibの優位性:

  • メソッドチェーンが使える: path.resolve().parent / "config.ini"
  • 関数の引数が減り、コードが簡潔に
  • IDEの補完が効きやすい

よくあるエラーと対処法

ValueError: base_pathが含まれていない

# エラーになるケース
abs_path = pathlib.Path("C:/projects/myapp/main.py")
rel = abs_path.relative_to("D:/other")  # ValueError!

対処法:

  • 基底パスが絶対パスに実際に含まれているか確認
  • Windows環境では異なるドライブ間では変換不可

存在しないパスの判定ミス

path = pathlib.Path("nonexistent.txt")
print(path.is_file())  # False(存在しないため)
print(path.is_dir())   # False(存在しないため)

対処法:

  • 先にexists()で存在確認してから判定する
if path.exists():
    if path.is_file():
        print("ファイルです")
    elif path.is_dir():
        print("ディレクトリです")
else:
    print("存在しません")

まとめ:pathlib活用でクロスプラットフォーム対応を実現

本記事では、PythonでOSに依存しないパス操作を実現するpathlibモジュールについて解説しました。

重要ポイント:

  • pathlibはPython 3.4以降で標準搭載されており、追加インストール不要
  • WindowsとMac/Linuxのパス表記の違いを自動的に吸収
  • オブジェクト指向の設計で、os.pathより可読性が高い
  • resolve()relative_to()絶対パス・相対パスの変換が簡単
  • 実務では専用クラスにラップすることでさらに使いやすくなる

WindowsとMacの両方で動作するPythonスクリプトを書く際は、文字列ベースのパス操作は避け、pathlibを積極的に活用しましょう。