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

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

使用するソースコード

以下の公開リポジトリに置いています

GitHub - masayan1126/tao-py-py: pythonリポジトリ
pythonリポジトリ. Contribute to masayan1126/tao-py-py development by creating an account on GitHub.

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

3種類もあります

  • os.path
  • glob
  • pathlib
masayan
masayan

個人的には、パスをオブジェクト指向で扱う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'
masayan
masayan

内部的には、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)
masayan
masayan
この絶対パスは、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
masayan
masayan

ただし、存在しないパスを渡した場合は固定で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))
masayan
masayan

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

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

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

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

Python学習におすすめの書籍

独習Python/山田祥寛【3000円以上送料無料】
bookfan 1号店 楽天市場店
¥ 3,300(2022/02/05 17:20時点)
タイトルとURLをコピーしました