Masayan tech blog.

  1. ブログ記事一覧>
  2. PythonでOSに依存せずディレクトリやファイルのパスを扱う方法

PythonでOSに依存せずディレクトリやファイルのパスを扱う方法

公開日

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

ディレクトリ・ファイルパスを扱うことができるモジュール

3種類もあります

  • os.path
  • glob
  • pathlib

個人的には、パスをオブジェクト指向で扱うpathlibがおすすめです。pathlibの使用には、python3.4以上が必要になります

前提知識

  • MacなどのUnix系OSでは/から始まり、Windowsではドライブ文字や\\から始まります
  • 絶対パスはルートディレクトリからのパス
    Windowsであれば「C:¥」や「D:¥」、macやLinuxは「/」がルート
  • 相対パスは現在位置を起点としたパス

pathlibの使い方

path文字列からpathオブジェクトを生成する

pathlibでは、以下のようにpathオブジェクトを生成するところから始まります

import pathlib

path = pathlib.Path("/var/www/html") # WindowsPath('/var/www/html')

pathオブジェクトは、以下のようにstr()にパラメータとして渡してあげることで、パス文字列に変換することが可能です

import pathlib

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

内部的には、osによって異なるpathオブジェクトが生成されます

  • windowsならWindowsPath
  • macならPosixPath

pathlibを使うことにより、Pathオブジェクト生成時のパラメータとして渡すパス文字列は相対パスでも絶対パスでもよく、なおかつパスの記載方法もOSに依存していないため、上記のようにwindowsでもパスを扱うことができています

現在の作業ディレクトリの絶対パスを取得する

cwd()を使用することでカレントワーキングディレクトリの絶対パスを取得できます

import pathlib

path = pathlib.Path.cwd()
path_str = str(path) # 'C:\\Users\\・・・'

実際に存在するパスかチェックする

exists()を使用するとことで、存在する場合はTrueが存在しない場合はFalseが返ります

import pathlib

exsits = pathlib.Path.cwd().exists() # True

絶対パスかどうかチェックする

import pathlib

exsits = pathlib.Path.cwd().is_absolute() # True

相対パスかどうかチェックする

こちらはメソッドがありません・・・

絶対パスを相対パスに変換

relative_to(起点となるパス文字列)とすることで、もともとの絶対パスから基底パスを除いた相対パスとして変換することが可能です

import pathlib

abs_path = pathlib.Path("/var/www/html")
relative_path = abs_path.relative_to("/var") # www/html

相対パスを絶対パスに変換

resolve()を使用することで、相対パスを絶対パスに変換することが可能です

import pathlib

relative_path = pathlib.Path("hoge")
abs_path = relative_path.resolve()
path_str = str(abs_path)

この絶対パスは、cwdに元の相対パスを結合した結果になります

パス文字列の追加(結合)

パスオブジェクトに対して、joinpath()を使用することでパスを追加できます

import pathlib

path = pathlib.Path("hoge").joinpath("foo.txt")
path_str = str(path) # 'hoge\\foo.txt'

パスがディレクトリか、ファイルかをチェックする

import pathlib

is_file = pathlib.Path("hoge").is_file() # False
is_dir = pathlib.Path("hoge").is_dir() # True

ただし、存在しないパスを渡した場合は固定でFalseが返るのでその点だけ注意が必要です

パスを扱う自作クラス

パスの処理に限ったことではないですが、こういうのは専用のクラスを用意するのが吉なので、上記のような基本的な処理をメソッドとして備えた以下のようなクラスを作成しています

shared\Domain\x_file_system_path.py

import pathlib

from shared.Domain.xstr import XStr

class XFileSystemPath:
    def __init__(self, path_str: XStr):
        # 引数で受け取ったパス文字列(絶対・相対どちらでもOK)からパスオブジェクトを生成
        self._path = pathlib.Path(path_str.get_string())

    def path(self) -> pathlib.Path:
        return self._path

    def of_text(self) -> str:
        return str(self.path())

    # 相対パス(cwdを起点とする相対パス)
    # Ex) #'tests\\x_file_system_path.py'
    def to_relative(self, base_path=None):
        if base_path is None:
            base_path = self.path().cwd()

        try:
            return XFileSystemPath(
                XStr(str(self.to_absolute().path().relative_to(base_path)))
            )
        except:
            # 相対パスに変換しようとしたパスに、起点となるcwdが含まれていない場合例外(つまり、もとの絶対パスに起点となるcwdが含まれている必要がある)
            raise ValueError

    # 絶対パス(cwd + self.path())
    # Ex) #'C:\\Users\\・・・\\tests\\x_file_system_path.py'
    def to_absolute(self):
        return XFileSystemPath(XStr(str(self.path().resolve())))

    # 現在の作業ディレクトリ(カレントワーキングディレクトリ)の絶対パス
    @staticmethod
    def cwd():
        return XFileSystemPath(XStr(str(pathlib.Path.cwd())))

    # ユーザーのホームディレクトリの絶対パス
    # Ex) 'C:\\Users\\nishigaki'
    @staticmethod
    def home_dir():
        return XFileSystemPath(XStr(str(pathlib.Path.home())))

    def exsits(self):
        return self.path().exists()

    # 相対パスかを判定するメソッドはpathilibにはなし
    def is_absolute(self) -> bool:
        return self.path().is_absolute()

    # ディレクトリかどうかを返します
    # 存在しないパスは強制的にFalse
    def is_file(self) -> bool:
        return self.path().is_file()

    # ファイルかどうかを返します
    # 存在しないパスは強制的にFalse
    def is_dir(self) -> bool:
        return self.path().is_dir()

    # パス文字列を追加して返します
    def join(self, *more_path_strs: str):
        joined_path_str = str(self.path().joinpath(*more_path_strs))
        return XFileSystemPath(XStr(joined_path_str))

本記事とは直接関係はないですが、joinメソッドの引数は可変長にしています。理由は追加したいパス文字列の数を呼び出し側から指定したいためです

Pythonでは、リストや辞書に*をつけると、それらを展開して引数として受け取ることが可能です。これを可変長引数といいます

また、pathlib.Path.home()を使用すると、homeディレクトリを取得することが可能です。

  • windows
    'C:\\Users\\username'
  • mac(Ubuntu)
    '/home/username'

まとめ

いかがでしたでしょうか。本記事では、PythonでOSに依存せずディレクトリやファイルのパスを扱う方法について紹介しています。windowsとmac(やlinux)ではパス文字列の書き方に差異があるため、何かしらのモジュールを使用しない場合はosに依存してしまいます。pathlibを使用してosに依存しないパスの管理がおすすめです