Python, pathlibの使い方(パスをオブジェクトとして操作・処理)

Modified: | Tags: Python, ファイル処理

Pythonのpathlibモジュールを使うと、ファイル・ディレクトリ(フォルダ)のパスをオブジェクトとして操作・処理できる。

ファイル名・親ディレクトリの抽出やパス一覧の取得、ファイルの作成・削除など、一通りの処理が可能。慣れるとパス文字列を利用する従来のos.pathよりも使いやすい。

ここではpathlibモジュールの基本的な使い方を説明する。

本記事のサンプルコードでは以下のようにpathlibモジュールをインポートしている。標準ライブラリに含まれているので追加のインストールは不要。

import pathlib

以下のようにPathクラスのみをインポートする例も多い。この場合、pathlib.Path()Path()とする。

from pathlib import Path

以下のようなファイル・ディレクトリ構成を例とする。

temp/
├── dir/
│   └── sub_dir/
│       └── file2.txt
└── file.txt

pathlibを使った具体的な処理については以下の記事などを参照。

Pathオブジェクトを生成・処理

コンストラクタpathlib.Path()

pathlibモジュールではパスをオブジェクトとして操作する。

コンストラクタpathlib.Path()Pathオブジェクトを生成できる。引数にパスの文字列を指定する。相対パスでも絶対パスでもよい。

p_file = pathlib.Path('temp/file.txt')
print(p_file)
# temp/file.txt

print(type(p_file))
# <class 'pathlib.PosixPath'>

引数を省略すると、現在の作業ディレクトリ(カレントディレクトリ)を表す相対パス.となる。pathlib.Path('.')と等価。

print(pathlib.Path())
# .

print(pathlib.Path() == pathlib.Path('.'))
# True

pathlib.Path()は実行環境に応じたクラスのインスタンスを生成する。上の例はMacで実行しているので、Unix系OSのクラスであるPosixPathのインスタンスが生成される。Windowsで実行するとWindowsPathとなる。

PosixPathWindowsPathPathのサブクラス。

print(issubclass(pathlib.PosixPath, pathlib.Path))
# True

print(issubclass(pathlib.WindowsPath, pathlib.Path))
# True

多くの場合、PosixPathWindowsPathを意識して使い分ける必要はなく、本記事においてもPosixPathWindowsPathのオブジェクトをまとめてPathオブジェクトと呼ぶ。

具象パス(Path)と純粋パス(PurePath)

Pathは具象パスと呼ばれ、その親クラスとして純粋パスPurePathがある。

PurePathはファイルシステムへのアクセス(I/O)を伴わない処理を提供し、PathはI/O処理も提供する。PathPurePathのサブクラスなので、PurePathのメソッドや属性もPathから全て使用できる。

print(issubclass(pathlib.Path, pathlib.PurePath))
# True

Unixマシン上でWindows形式のパスを扱いたい(またはその逆)といった特別な場合は、純粋パスのクラスであるPurePosixPathPureWindowsPathのインスタンスを生成する必要があるが、実際のファイルやディレクトリを処理する場合はpathlib.Path()を使えばよい。

Pathのメソッドと属性

Pathオブジェクトのメソッドや属性によって様々な処理が可能となる。

例えば、パスが示すのがファイルか判定するにはis_file()メソッド、拡張子を文字列で取得するにはsuffix属性を使う。

p_file = pathlib.Path('temp/file.txt')

print(p_file.is_file())
# True

print(p_file.suffix)
# .txt

なお、suffixPurePathの属性であるが、上述のようにPurePathを継承するPathのインスタンスからも使用可能。

このように、所望のファイルやディレクトリを示すパスのPathオブジェクトを生成しメソッドや属性によって操作する、というのがpathlibを使った処理の基本的な流れとなる。

存在しないパスに対する処理

存在しないパスのオブジェクトを生成することもできる。パスの存在を確認するexists()メソッドがFalseを返す。

p_new_file = pathlib.Path('temp/new_file.txt')

print(p_new_file.exists())
# False

存在しないパスのオブジェクトから新しいファイルやディレクトリを作成できる。例えば、touch()メソッドは空のファイルを作成する。

p_new_file.touch()

print(p_new_file.exists())
# True

メソッドをつなげて一行で書いてもよい。

pathlib.Path('temp/new_file2.txt').touch()

ディレクトリ直下のパス一覧のイテレータを取得するiterdir()メソッドを使うと、ファイルが新規作成されていることが確認できる。

for p in pathlib.Path('temp').iterdir():
    print(p)
# temp/file.txt
# temp/new_file.txt
# temp/new_file2.txt
# temp/dir

pathlibを使ったファイル・ディレクトリの作成や一覧の取得などについての詳細は以下の記事を参照。

パスの移動: /演算子, joinpath(), parentなど

ディレクトリツリー内を移動するように、あるPathオブジェクトを基準として別のディレクトリやファイルを示すPathオブジェクトを生成できる。

パスの連結・追加

Pathオブジェクトに対して/演算子を使うとパスが連結される。

p_dir = pathlib.Path('temp/dir')

p_sub_dir_file = p_dir / 'sub_dir' / 'file2.txt'
print(p_sub_dir_file)
# temp/dir/sub_dir/file2.txt

print(p_sub_dir_file.is_file())
# True

joinpath()メソッドでも同様にパスを連結できる。複数連結する場合は引数を複数指定する。os.path.join()に相当する。

p_sub_dir_file = p_dir.joinpath('sub_dir', 'file2.txt')
print(p_sub_dir_file)
# temp/dir/sub_dir/file2.txt

print(p_sub_dir_file.is_file())
# True

親ディレクトリへの移動

..の連結と相対パス・絶対パス

相対パス..を連結すると親ディレクトリ(上位ディレクトリ)への移動となる。

p_dir = pathlib.Path('temp/dir')

p_file_join = p_dir.joinpath('..', 'file.txt')
print(p_file_join)
# temp/dir/../file.txt

パスが参照するファイルが同一か判定するsamefile()メソッドで、..を使わないPathオブジェクトと同一のファイルを参照していることが確認できる。この場合、==演算子はFalseを返すので注意。

p_file = pathlib.Path('temp/file.txt')

print(p_file.samefile(p_file_join))
# True

print(p_file == p_file_join)
# False

..を含む相対パスを絶対パスに変換するにはresolve()メソッドを使う。resolve()で絶対パスに変換すると==Trueとなる。

print(p_file_join.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/temp/file.txt

print(p_file.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/temp/file.txt

print(p_file_join.resolve() == p_file.resolve())
# True

絶対パスを相対パスに変換するにはrelative_to()メソッドを使う。引数に指定したパスを基準とする相対パスを返す。

Pythonが実行されている作業ディレクトリ(カレントディレクトリ)はcwd()で取得できるので、絶対パスをカレントディレクトリからの相対パスに変換するには以下のようにする。

print(p_file_join.resolve().relative_to(pathlib.Path.cwd()))
# temp/file.txt

pathlibを使った相対パス・絶対パスの処理についての詳細は以下の記事を参照。

parent属性

親ディレクトリに移動するにはparent属性を使う方法もある。

p_dir = pathlib.Path('temp/dir')

print(p_dir.parent)
# temp

print(p_dir.parent.joinpath('file.txt'))
# temp/file.txt

ただし、parentは純粋な字句操作なので注意。元のオブジェクトが..を含んでいる場合、特にそれらを解釈するわけではない。

p_file_join = p_dir.joinpath('..', 'file.txt')
print(p_file_join)
# temp/dir/../file.txt

print(p_file_join.parent)
# temp/dir/..

print(p_file_join.parent.parent)
# temp/dir

上位階層に一気に移動できるparentsなど、より詳細は以下の記事を参照。

with_name()メソッド

with_name()メソッドを使うと、同じディレクトリ(同一階層)の別名のファイルのPathを生成できる。親ディレクトリに移動して別のファイル名を連結するより簡単。

p_file = pathlib.Path('temp/file.txt')

print(p_file.with_name('new_file.txt'))
# temp/new_file.txt

拡張子を変更するwith_suffix()など、より詳細は以下の記事を参照。

Pathオブジェクトを文字列(str)に変換

これまでの例のように、Pathオブジェクトをprint()で出力するとパスの文字列が表示されるが、型自体はあくまでもPathPosixPathまたはWindowsPath)で、文字列(str)ではない。

p_file = pathlib.Path('temp/file.txt')
print(p_file)
# temp/file.txt

print(type(p_file))
# <class 'pathlib.PosixPath'>

文字列に変換したい場合はstr()を使う。

s = str(p_file)
print(s)
# temp/file.txt

print(type(s))
# <class 'str'>

Pathname属性でファイル名を文字列で取得したり、suffix属性で拡張子を文字列で取得したりすることもできる。

osモジュールの関数の引数にPathオブジェクトを指定

os.pathの関数の引数にはパスの文字列を指定するが、Python3.6以降、多くの関数でPathオブジェクトも引数に指定できるようになった。

os.path.isfile()を例とする。パス文字列でもPathオブジェクトでも正しく動作する。

import os

print(os.path.isfile('temp/file.txt'))
# True

print(os.path.isfile(pathlib.Path('temp/file.txt')))
# True

Pathオブジェクトを引数に指定できるようになった関数には、その旨が公式ドキュメントに記載されている。

バージョン 3.6 で変更: path-like object を受け入れるようになりました。
os.path.isfile() — Python 3.12.0 ドキュメント

なお、上述のようにPathオブジェクトにはos.path.isfile()に対応するis_file()メソッドがある。次に説明するようにosの主な関数にはそれに対応するPathオブジェクトのメソッドがあるので、あえてosの関数を使う必要はあまりない。

osモジュールとpathlibモジュールの対応一覧

osモジュールの関数がpathlibモジュールのPathおよびPurePathオブジェクトのどのメソッドに対応しているかの一覧表が公式ドキュメントに記載されている。

主なものは以下の通り。

処理内容 os および os.path pathlib
カレントディレクトリ取得 os.getcwd() Path.cwd()
先頭の~をホームディレクトリに置換 os.path.expanduser() Path.expanduser(), Path.home()
パスの存在確認 os.path.exists() Path.exists()
ディレクトリか判定 os.path.isdir() Path.is_dir()
ファイルか判定 os.path.isfile() Path.is_file()
シンボリックリンクか判定 os.path.islink() Path.is_symlink()
絶対パスか判定 os.path.isabs() PurePath.is_absolute()
絶対パスに変換 os.path.realpath() Path.resolve()
相対パスに変換 os.path.relpath() PurePath.relative_to()
ステータスを取得 os.stat() Path.stat(), Path.owner(), Path.group()
パスを連結 os.path.join() PurePath.joinpath()
ファイル名を取得 os.path.basename() PurePath.name
親ディレクトリを取得 os.path.dirname() PurePath.parent
拡張子を分割・取得 os.path.splitext() PurePath.stem, PurePath.suffix
ディレクトリを生成 os.makedirs() Path.mkdir()
ディレクトリを削除 os.rmdir() Path.rmdir()
ファイルを削除 os.remove(), os.unlink() Path.unlink()

それぞれのメソッドや属性の使い方の詳細は以下の記事を参照。

関連カテゴリー

関連記事