Python, pathlibで絶対パスと相対パスを相互変換・判定
Pythonのpathlibモジュールを使って絶対パスと相対パスを相互に変換したり判定したりする方法を説明する。
pathlibの基本的な使い方については以下の記事を参照。
本記事のサンプルコードでは以下のようにpathlibモジュールからPathクラスをインポートしている。pathlibは標準ライブラリに含まれているので追加のインストールは不要。
from pathlib import Path
絶対パスと相対パス
はじめに絶対パスと相対パスについて簡単に説明する。
Wikipedia英語版に具体的な例を含む説明がある。
絶対パス(absolute path)
絶対パスは、ファイルやディレクトリを一意に示すパス。
Macを含むUnix系OSでは/から始まり、Windowsではドライブ文字や\\(サーバーの場合)から始まる。
- Unix(Mac含む):
/Users/xxx/Documents/ - WIndows:
C:\Program Filesor\\servername\
相対パス(relative path)
相対パスは、あるディレクトリ(カレントディレクトリ)からの相対的な位置を示すパス。カレントディレクトリによって同じファイルやディレクトリでも異なるパスで表される。
例えば、/Users/xxx/Documents/file.txtというファイルの相対パスは以下のように表される。
/Users/をカレントディレクトリとした場合xxx/Documents/file.txt
/Users/xxx/をカレントディレクトリとした場合Documents/file.txt
相対パスでは現在のパスを明示的に示す.や、一つ上の階層を示す..などが使える。
/Users/xxx/Documents/をカレントディレクトリとした場合、..は/Users/xxx/に相当する。
pathlibの基本
Path()でPathオブジェクトを生成して操作する。引数に相対パスまたは絶対パスを文字列で指定する。
p = Path('dir/sub_dir/file.txt')
print(p)
# dir/sub_dir/file.txt
print(type(p))
# <class 'pathlib.PosixPath'>
OSによってPosixPathまたはWindowsPathのインスタンスが生成される。
なお、以降では純粋パスPurePathのメソッドも紹介するが、具象パスPathは純粋パスPurePathのサブクラスなので、Path(PosixPathまたはWindowsPath)のインスタンスからでも使用可能。
詳しくは以下の記事を参照。
カレントディレクトリを取得: cwd()
Pathのクラスメソッドであるcwd()でカレントディレクトリの絶対パスのPathオブジェクトを取得できる。
print(Path.cwd())
# /Users/mbp/Documents/my-project/python-snippets/notebook
print(type(Path.cwd()))
# <class 'pathlib.PosixPath'>
Pathのインスタンスからもcwd()を呼べるようになっている。
p = Path('dir/sub_dir/file.txt')
print(p.cwd())
# /Users/mbp/Documents/my-project/python-snippets/notebook
print(type(p.cwd()))
# <class 'pathlib.PosixPath'>
同じくカレントディレクトリを返すos.getcwd()はパスを文字列(str)で返す。
import os
print(os.getcwd())
# /Users/mbp/Documents/my-project/python-snippets/notebook
print(type(os.getcwd()))
# <class 'str'>
pathlibにはカレントディレクトリを変更するメソッドはない。os.chdir()を使う。os.chdir()の引数にはPathオブジェクトをそのまま指定可能。
相対パスを絶対パスに変換: resolve(), absolute()
resolve()
相対パスを絶対パスに変換するにはresolve()を使う。Pathオブジェクトが返される。
p = Path('dir/sub_dir/file.txt')
print(p.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt
相対パスに..が含まれている場合、正しく解釈された上で除去されて絶対パスに変換される。
p_rel = Path('dir/sub_dir/../file.txt')
print(p_rel)
# dir/sub_dir/../file.txt
print(p_rel.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/file.txt
引数strictをTrueとすると、パスが示すファイルやディレクトリが存在しない場合にエラーとなる。デフォルトはstrict=False。
p_new_file = Path('data/new_file.txt')
print(p_new_file.resolve())
# /Users/mbp/Documents/my-project/python-snippets/notebook/data/new_file.txt
# print(p_new_file.resolve(strict=True))
# FileNotFoundError: [Errno 2] No such file or directory: 'data/new_file.txt'
例は省略するが、resolve()はシンボリックリンクも解決する。
absolute()
absolute()も相対パスを絶対パスに変換する。Pathオブジェクトが返される。
p = Path('dir/sub_dir/file.txt')
print(p.absolute())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt
absolute()はresolve()と異なり..を除去しない。
p_rel = Path('dir/sub_dir/../file.txt')
print(p_rel.absolute())
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/../file.txt
absolute()には引数strictはなく、シンボリックリンクも解決しない。
絶対パスを相対パスに変換: relative_to()
以下の絶対パスを表すPathオブジェクトを例とする。
p_abs = Path('dir/sub_dir/file.txt').resolve()
print(p_abs)
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt
絶対パスを相対パスに変換するにはrelative_to()を使う。
引数に指定したパスを起点とする相対パスに変換される。
カレントディレクトリを起点とする相対パスに変換したい場合は上述のcwd()を使う。
print(p_abs.relative_to(Path.cwd()))
# dir/sub_dir/file.txt
引数はPathオブジェクトではなくパスの文字列でも指定可能。
print(p_abs.relative_to('/Users/mbp/Documents/my-project'))
# python-snippets/notebook/dir/sub_dir/file.txt
ルートやドライブが異なっているなど、変換が不可能なディレクトリを引数に指定するとエラーとなる。
# print(p_abs.relative_to('/usr/'))
# ValueError: '/Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt' is not in the subpath of '/usr' OR one path is relative and the other is absolute.
便宜上、絶対パスを相対パスに変換と書いたが、相対パスを別のパスを起点とする相対パスに変換することもできる。
p = Path('dir/sub_dir/file.txt')
print(p.relative_to('dir'))
# sub_dir/file.txt
絶対パスか判定: is_absolute()
Pathオブジェクトが絶対パスかを判定するにはis_absolute()を使う。
p_abs = Path('dir/sub_dir/file.txt').resolve()
print(p_abs)
# /Users/mbp/Documents/my-project/python-snippets/notebook/dir/sub_dir/file.txt
print(p_abs.is_absolute())
# True
is_absolute()がFalseであれば絶対パスではなく相対パス。
p = Path('dir/sub_dir/file.txt')
print(p.is_absolute())
# False
なお、Python 3.9で追加されたis_relative_to()は現在のパスが引数に指定したパスからの相対パスとして表せるか(relative_to()を実行できるか)を判定するもので、相対パスかどうかを判定するものはない。
print(p_abs.is_relative_to(Path.cwd()))
# True
print(p.is_relative_to(Path.cwd()))
# False
print(p.is_relative_to('dir/sub_dir'))
# True