pathlibモジュールが提供するPathクラスのabsoluteメソッドとresolveメソッドはどちらも絶対パスを得るためのものである。その違いや使い分けについて紹介する。
import os
from pathlib import Path
d = Path.cwd() # d = os.getcwd()
print(d)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199
# Windows:
# カレントディレクトリを基点とした相対パスを取得
p = Path('foo.txt').absolute()
print(p) # /private/tmp/pytips/pytips_0199/foo.txt
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/foo.txt
# Windows:C:\tmp\pytips\pytips_0199
print(type(p))
# 出力結果:
# macOS:<class 'pathlib.PosixPath'>/<class 'pathlib._local.PosixPath'>
# Windows:<class 'pathlib.WindowsPath'>/<class 'pathlib._local.WindowsPath'>
# Path.absoluteメソッドは「..」やシンボリックリンクなどを解決しないまま返す
p = Path('dir0/../pytips_0199.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/../pytips_0199.txt
# Windows:C:\tmp\pytips\pytips_0199\dir0\..\pytips_0199.txt
# Path.resolveメソッドは「..」やシンボリックリンクを解決する
p = p.resolve()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/pytips_0199.txt
# Windows:C:\tmp\pytips\pytips_0199\pytips_0199.txt
# シンボリックリンクの解決
Path('dir1').is_symlink() # True
p = Path('dir1/foo.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir1/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir1\foo.txt
os.chdir('dir1')
p = Path('foo.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir1\foo.txt
os.chdir('..')
p = Path('dir1/foo.txt').resolve()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir0\foo.txt
以下では次のようなディレクトリ階層でpytips_0199ディレクトリが現在の作業ディレクトリとなっているものとする。macOSでは/tmp/pytipsディレクトリ以下に次のようなディレクトリ階層を作成した。dir1ディレクトリはdir0ディレクトリへのシンボリックリンクとなっている。
WindowsではC:\tmp\pytipsディレクトリ以下に次のようなディレクトリ階層を作成した。
macOSのサンプルディレクトリ階層と同様、dir1ディレクトリはdir0ディレクトリへのシンボリックリンクとなっている(管理者権限でコマンドプロンプトを開き「mklink /D dir1 dir0」コマンドで作成)。
また、事前にosモジュールをインポートし、pathlibモジュールからPathクラスもインポートしておく。
import os
from pathlib import Path
d = Path.cwd() # d = os.getcwd()
print(d)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199
# Windows:C:\tmp\pytips\pytips_0199
macOSでのPath.cwdクラスメソッドの出力結果を見ると、「/tmp/…‥」ではなく「/private/tmp/…‥」となっているのは/tmpが/private/tmpへのシンボリックリンクとなっているからなので、以降ではこれを/tmpと読み替えてほしい。
pathlibモジュールにはファイルパスを表すPathクラス(とその具象クラス)があり、そのメソッドとしてパス関連の機能が用意されている。現在の作業ディレクトリを基点として、特定のパスの絶対パスを取得するにはそれらのうちのabsoluteメソッドを使用する。
以下にシンプルな例を示す。
p = Path('foo.txt').absolute()
print(p) # /private/tmp/pytips/pytips_0199/foo.txt
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/foo.txt
# Windows:C:\tmp\pytips\pytips_0199
ここではfoo.txtというパスを表すオブジェクトを作成し、absoluteメソッドを呼び出している(現在の作業ディレクトリにはこのファイルは存在しないが、抽象的なパス操作をしている分には問題はない)。これにより、現在の作業ディレクトリを基点としたfoo.txtの絶対パスが返される。
返されるのはPathクラス(の具象クラス)のオブジェクトである(os.pathモジュールの関数は文字列を返す)。
print(type(p))
# 出力結果:
# macOS:<class 'pathlib.PosixPath'>/<class 'pathlib._local.PosixPath'>
# Windows:<class 'pathlib.WindowsPath'>/<class 'pathlib._local.WindowsPath'>
なお、2024年10月9日の時点では、Python 3.13.0環境でこれを実行すると、pathlib._local.PosixPathやpathlib._local.WindowsPathのように「_local」を間に含むようになる(Python 3.13ではpathlibモジュールに手が加えられた結果と思われる)。
Path.absoluteメソッドは自身のパスに現在のディレクトリを表すドット「.」や親ディレクトリを表す「..」、シンボリックリンクなどが含まれていても、それらを解決しない点には注意しよう。以下に例を示す。
p = Path('dir0/../pytips_0199.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/../pytips_0199.txt
# Windows:C:\tmp\pytips\pytips_0199\dir0\..\pytips_0199.txt
この例ではカレントディレクトリの下にあるdir0ディレクトリ、dir0の親ディレクトリを示す「..」、最後にpytips_0199.txtという3つの要素で構成されるパスを表すPathオブジェクトを作成して、absoluteメソッドを呼び出している。このとき、「..」が解決されていれば、得られる絶対パスは「/private/tmp/pytips/pytips_0199/pytips_0199.txt」「C:\tmp\pytips\pytips_0199\pytips_0199.txt」のようになるが、実際に得られるのは「..」入りのパスとなる。
このようなパスから「..」などを削除して、シンボリックリンクを解決した絶対パスを得たいのであれば、Path.resolveメソッドを使用する。以下は「..」を含むパスからそれを削除する例だ。
p = p.resolve()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/pytips_0199.txt
# Windows:C:\tmp\pytips\pytips_0199\pytips_0199.txt
次にシンボリックリンクを解決する例も見ておこう。既に述べた通り、dir1ディレクトリはdir0ディレクトリへのシンボリックリンクである。そのため、そのパスに対して、Path.is_symlinkメソッドを呼び出すとTrueが返される。
Path('dir1').is_symlink() # True
このシンボリックリンクを含むPath('dir1/foo.txt')というパスに対して、absoluteメソッドを呼び出すと次のようになる。
p = Path('dir1/foo.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir1/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir1\foo.txt
macOSでもWindowsでもシンボリックリンクは解決されずに、そのままdir1として絶対パスが返されている。
この状態で、dir1ディレクトリに移動して、そこにあるfoo.txtファイルを指すパスであるPath('foo.txt')でabsoluteメソッドを呼び出したのが以下だ。
os.chdir('dir1')
p = Path('foo.txt').absolute()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir1\foo.txt
macOSではdir1ではなく、dir0が絶対パスに含まれるようになった。Windowsではシンボリックリンクであるdir1が絶対パスに含まれたままとなる(プラットフォームごとの挙動の異なり)。
ここで、ディレクトリを元に戻して、Path('dir1/foo.txt')というパスに対してresolveメソッドを呼び出すと、今度はシンボリックリンクが解決され、dir0を含む絶対パスが(macOSでもWindowsでも)取得できた。
os.chdir('..')
p = Path('dir1/foo.txt').resolve()
print(p)
# 出力結果:
# macOS:/private/tmp/pytips/pytips_0199/dir0/foo.txt
# Windows:C:\tmp\pytips\pytips_0199\dir0\foo.txt
普段はabsoluteメソッドで絶対パスを取得すればよいだろうが、パスの正規化(「..」の削除など)が必要になったり、シンボリックリンクを解決したりする必要があるときにはresolveメソッドを呼び出す必要がある。
Copyright© Digital Advantage Corp. All Rights Reserved.