Pythonでパスを扱うモジュールとしてos.pathモジュールが提供されていましたが、このos.pathモジュールの機能を置き換えるpathlibモジュールがPython 3.4から新しく提供されています。
os.pathモジュールはパスを文字列として扱いますが、pathlibモジュールではより高水準なオブジェクトとしてパスを扱います。pathlibモジュールは、os.pathモジュールを置き換えるだけでなく、osモジュールで提供されている機能の一部やファイル入出力機能も提供し、パスに関する包括的なモジュールになっています。
この記事ではpathlibモジュールの使い方を基礎から詳しく解説していきます。os.pathモジュールについては次の記事を参照ください。
pathlibモジュールが提供するクラス
pathlibモジュールにはいくつかのクラスが提供されています。pathlibモジュールでは、パスをこれらのクラスのオブジェクトで扱いますので、最初にこれらのクラスを理解する必要があります。
pathlibモジュールのクラスと継承関係
pathlibモジュールが提供するクラスは、次の2種類に分類することができます。
- 純粋パス
- 具象パス
pathlibモジュールで定義されたクラスの内で、純粋パスに分類されるクラスは次の3つです。
- PurePathクラス
- PureWindowsPathクラス(PurePathクラスを継承)
- PurePosixPathクラス(PurePathクラスを継承)
具象パスに分類されるクラスは次の3つです。
- Pathクラス(PurePathクラスを継承)
- WindowsPathクラス(PureWindowsPathクラスとPathクラスを継承)
- PosixPathクラス(PurePosixPathクラスとPathクラスを継承)
これらのクラスの継承関係を図で示すと次のようになります。純粋パスのクラスは青、具象パスのクラスは緑で示しています。
具象パスのクラスは純粋パスのクラスを継承しているので、純粋パスの機能を継承し、さらに独自の機能を追加しています。
Windows形式のパスとUnix系OS形式のパスを表すクラス
これらのクラスの内、実際にインスタンス化して使用するクラスは次の4つです。
- PureWindowsPathクラス
- PurePosixPathクラス
- WindowsPathクラス
- PosixPathクラス
名前が示す通り、PureWindowsPathとWindowsPathはWindows形式のパスを表すオブジェクトです。一方、PurePosixPath、PosixPathはUnix系OS(Windows以外)が使う形式のパスを表すオブジェクトです。
Windows形式の(絶対)パスは次のような形式です。
C:\Users\taro
Unix系OS(Windows以外)の(絶対)パスは次のような形式です。
/home/taro
これらのパス形式については前述のos.pathモジュールの記事で解説していますので、よくわからない方はご参照ください。
純粋パスと具象パス
具象パスのオブジェクトは、実際にプログラムを実行しているOSへアクセスする機能を提供します。そのため、Windows環境で実行するPythonプログラムでUnix系OS形式のパスを表すPosixPathクラスをインスタンス化することはできません(エラーになります)。反対に、Unix系OSでWindowsPathクラスをインスタンス化することもできません。
純粋パスのオブジェクトはOSへのアクセスを伴わず、純粋にパスを操作する機能のみを提供するのでこのような制約はありません。つまり、Windows環境で実行するプログラムで、PurePosixPathクラスをインスタンス化して、そのオブジェクトが表すパスを操作することができます。
pathlibモジュールのクラスのまとめ
このようにpathlibモジュールにはいくつものクラスがあり、用途によって使い分ける必要がありますが、あまり難しく考える必要はありません。大抵はプログラムを実行する環境のパス形式を表すクラスを使います。
- Windowsでプログラムを実行する場合→PureWindowsPathかWindowsPathクラスを使う
- Unix系OSでプログラムを実行する場合→PurePosixPathかPosixPathのオブジェクトを使う
そして純粋パスが提供する機能で十分であればそれらのクラス(PureWindowsPathかをPurePosixPath)使い、具象パスクラスが提供する機能も必要であればそれらのクラス(WindowsPathかPosixPath)を使います。
特別な場合として、プログラムを実行する環境のパス形式と異なる形式のパスを扱う場合は、純粋パスのクラス(PureWindowsPathかをPurePosixPath)しか使用できません。このようなケースはパスの文字列表現が必要な場合に限られるでしょう。
なお、すべてのパスクラスのオブジェクトはイミュータブルでハッシュ可能です。
パスクラスのインスタンスを作成する
pathlibモジュールでパスを扱う場合、最初にパスを表すパスクラスのインスタンスを作成します。ここでは、それらのインスタンスの作成方法を解説します。
純粋パスのインスタンスを作成する — PurePath()、PureWindowsPath()、PurePosixPath()
純粋パスクラスのインスタンスを作成する一つの方法は、PurePath()を使うことです。引数には0個以上のパス(の一部)を渡します。引数を省略するのはドット(.)を指定するのと等価です。
PurePath()はプログラムを実行するOSに応じたインスタンスを返します。つまりWindowsで実行するとPureWindowsPathのインスタンス、Unix系OSで実行するとPurePosixPathのインスタンスを返します。
>>> from pathlib import PurePath
>>> pp1 = PurePath('testdir/dir1/file.txt')
>>> pp2 = PurePath('testdir', 'dir1/file.txt')
>>> pp3 = PurePath('testdir', 'dir1', 'file.txt')
>>> pp1
PurePosixPath('testdir/dir1/file.txt')
上記の例はいずれも同じパス(testdir/dir1/file.txt )を表す純粋パスクラスのインスタンスを生成します。
プログラムを実行する環境に依存せずPureWindowsPathかPurePosixPathのインスタンスを作成したい場合は、明示的にそれらのインスタンスを作成します。
例えば実行環境によらずPureWindowsPathオブジェクトを作成したい場合はPureWindowsPath()を使います。引数にはPurePath()と同様、0個以上のパス(の一部)を渡します。引数を省略するとドット(.)を指定するのと等価です。
>>> from pathlib import PureWindowsPath
>>> pwp = PureWindowsPath('testdir/dir1/file.txt')
>>> type(pwp)
<class 'pathlib.PureWindowsPath'>
PurePosixPathオブジェクトを作成するにはPurePosixPath()を使います。
>>> from pathlib import PurePosixPath
>>> ppp = PurePosixPath('testdir/dir1/file.txt')
>>> type(ppp)
<class 'pathlib.PurePosixPath'>
具象パスクラスのインスタンスを作成する — Path()、WindowsPath()、PosixPath()
具象パスクラスのインスタンスを作成するにはPath()を使うことができます。引数には0個以上のパス(の一部)を渡します。引数を省略するのはドット(.)を指定するのと等価です。
Path()もプログラムを実行するOSに応じたインスタンスを返します。つまりWindowsで実行するとWindowsPathのインスタンス、Unix系OSで実行するとPosixPathのインスタンスを返します。
>>> from pathlib import Path
>>> p1 = Path('testdir/dir1/file.txt')
>>> p2 = Path('testdir', 'dir1/file.txt')
>>> p3 = Path('testdir', 'dir1', 'file.txt')
>>> p1
PosixPath('testdir/dir1/file.txt')
上記の例はいずれも同じパス(testdir/dir1/file.txt )を表す具象パスクラスのインスタンスを生成します。
Windows環境でプログラムを実行する場合は、WindowsPathのインスタンスを明示的に作成することもできます。
>>> from pathlib import WindowsPath
>>> wp = WindowsPath('testdir/dir1/file.txt')
Unix系OS環境で実行する場合は、PosixPosixPathのインスタンスを明示的に作成することもできます。
>>> from pathlib import PosixPath
>>> pp = PosixPath('testdir/dir1/file.txt')
WindowsでPosixPathのインスタンスを作成したり、Unix系OSでWindowsPathのインスタンスを作成しようとするとNotImplementedErrorエラーを送出します。これは具象パスのインスタンスがOSへのアクセスを提供するためです。
以下はUnix系OSでWindowsPathのインスタンスを生成しよとしたときのエラーです。
>>> from pathlib import WindowsPath
>>> wp = WindowsPath('testdir/dir1/file.txt')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1084, in __new__
raise NotImplementedError("cannot instantiate %r on your system"
NotImplementedError: cannot instantiate 'WindowsPath' on your system
パスオブジェクトの文字列表現
パスオブジェクトの文字列表現はUnix系OSのパスオブジェクト(PurePosixPathとPosixPath)の場合はUnix形式のパス、Windows形式のパスオブジェクト(PureWindowsPathとWindowsPath)の場合は、Windows形式のパスを表す文字列になります。
>>> ppp = PurePosixPath('/home/taro')
>>> ppp
PurePosixPath('/home/taro')
>>> str(ppp)
'/home/taro'
>>> pwp = PureWindowsPath('C:/Users/taro')
>>> pwp
PureWindowsPath('C:/Users/taro')
>>> str(pwp)
'C:\\Users\\taro'
末尾がパスセパレータで終わるパスオブジェクトは作成できない
パス末尾のディレクトリにパスセパレータが付いたパス文字列を見かけることがありますが、パスオブジェクトではそのようなパスを表す(作成する)ことはできません。
これは欠点というより、むしろ利点です。パスを文字列として扱うときに、末尾がパスセパレータで終わっているか考慮が必要な時がありますが、パスオブジェクトではパスセパレータで終わることがないのでそのような考慮は不要です。
>>> PurePath('/home/taro/testdir/')
PurePosixPath('/home/taro/testdir')
>>> Path('/home/taro/testdir/')
PosixPath('/home/taro/testdir')
ただし、ルートディレクトリはそれ自体がディレクトリを表しているので上記とは異なることに注意してください。
>>> PurePosixPath('/')
PurePosixPath('/')
>>> PureWindowsPath('C:/')
PureWindowsPath('C:/')
純粋パスクラスの属性とメソッド
純粋パスクラス(PureWindowsPath、PurePosixPathかPosixPath)には、そのインスタンスが表すパスの構成要素を参照するための属性や、パスの操作やパスの確認をするためのメソッドが定義されています。ここではそれら純粋パスクラスの機能についてみていきましょう。
パスの構成要素を参照する — parts、drive、root、anchor属性
パスを構成する個々の要素はparts属性で参照できます。これは個々の構成要素を要素とするタプルです。
>>> ppp = PurePosixPath('/home/taro/foo.txt')
>>> ppp.parts
('/', 'home', 'taro', 'foo.txt')
>>> pwp = PureWindowsPath('c:/user/jiro/bar.txt')
>>> pwp.parts
('c:\\', 'user', 'jiro', 'bar.txt')
PureWindowsPathオブジェクトの場合、ドライブ名とローカルルートは1つの要素にまとめられます。
drive属性は、ドライブ文字があればその文字列になります。
>>> ppp.drive
''
>>> pwp.drive
'c:'
root属性は、ローカルまたはグローバルルートを表す文字列があればそれになります。
>>> ppp.root
'/'
>>> pwp.root
'\\'
>>> PurePosixPath('taro/foo.txt').root
''
anchor属性は、ドライブ文字(もしあれば)とルートを結合した文字列になります。
>>> ppp.anchor
'/'
>>> pwp.anchor
'c:\\'
親ディレクトリの参照 — parent属性
parent属性は、親ディレクトリのパスです。
>>> p = PurePosixPath('/foo/bar')
>>> p.parent
PurePosixPath('/foo')
アンカーの位置を越えることや空のパスになる位置には対応していません。
>>> p = PurePosixPath('/foo')
>>> p.parent
PurePosixPath('/')
>>> p.parent.parent
PurePosixPath('/')
>>> p = PurePosixPath('.')
>>> p.parent
PurePosixPath('.')
parent属性は、単純に末尾の要素を削除した値になります。そのため、「/foo/..」は実際にはルートディレクトリ(/)を表すパスですが、parent属性は次のような値になります。
>>> p = PurePosixPath('/foo/..')
>>> p.parent
PurePosixPath('/foo')
パス要素の末尾の名前や拡張子 — name、suffix、suffixes、stem属性
name属性はパス要素の末尾を表す文字列です。ドライブ文字やルートは含まれません。
>>> PurePosixPath('docs/some/file.txt').name
'file.txt'
>>> PurePosixPath('docs/some').name
'some'
suffix属性はパスの末尾の要素の拡張子です。拡張子がなければ空文字列です。
>>> PurePosixPath('docs/some/file.txt').suffix
'.txt'
>>> PurePosixPath('docs/some/file.tar.gz').suffix
'.gz'
>>> PurePosixPath('docs/some/file').suffix
''
suffixes属性はパス要素の末尾のすべての拡張子のリストになります。
>>> PurePosixPath('docs/some/file.txt').suffixes
['.txt']
>>> PurePosixPath('docs/some/file.tar.gz').suffixes
['.tar', '.gz']
>>> PurePosixPath('docs/some/file').suffixes
[]
stem属性は末尾のパス要素から末尾の拡張子を除いたものになります。
>>> PurePosixPath('docs/some/file.txt').stem
'file'
>>> PurePosixPath('docs/some/file.tar.gz').stem
'file.tar'
>>> PurePosixPath('docs/some/file').stem
'file'
絶対パスか確認する — is_absolute()
is_absolute()は、引数に渡されたパスが絶対パスであればTrueを返し、そうでなければFalseを返します。パスが絶対パスとみなされるのは、ルートと Windowsであればドライブの両方が含まれる場合です。
>>> PurePosixPath('/home/taro').is_absolute()
True
>>> PurePosixPath('home/taro').is_absolute()
False
>>> PureWindowsPath('c:/home/taro').is_absolute()
True
>>> PureWindowsPath('/home/taro').is_absolute()
False
パスを結合する — joinpath()
joinpath()は、引数に渡された部分パスを現在のパスの後ろに順に繋げます。
>>> PurePosixPath('/home').joinpath('taro')
PurePosixPath('/home/taro')
>>> PurePosixPath('/home').joinpath('taro', 'sample.txt')
PurePosixPath('/home/taro/sample.txt')
>>> PureWindowsPath('c:').joinpath('/taro')
PureWindowsPath('c:/taro')
パターンにマッチするか確認する — match()
match()は、引数に渡されたグロブ形式のパターン文字列が現在のパスにマッチする場合はTrueを返し、そうでなければFalseを返します。
パターンが相対表記であれば右からマッチするかどうか調べられます。
>>> PurePath('foo/bar/baz.txt').match('*.txt')
True
>>> PurePath('/foo/bar/baz.txt').match('bar/*.txt')
True
>>> PurePath('/foo/bar/baz.txt').match('bar*.txt')
False
パターンが絶対表記であれば、対象パスは絶対パスでなければならず、パス全体がマッチしなければなりません。
>>> PurePath('/foo/bar/baz.txt').match('/*.txt')
False
>>> PurePath('/foo/bar/baz.txt').match('/*/*/*.txt')
True
パスの変更 — with_name()、with_stem()、with_suffix()
with_name()は現在のパスの name属性を引数の値に変更した、新しい純粋パスオブジェクトを返します。元のパスに name 属性がなければ ValueError が送出されます:
>>> p = PurePath('/foo/bar/baz.txt')
>>> p.with_name('hoge.py')
PurePosixPath('/foo/bar/hoge.py')
with_stem()は、現在のパスの stem 属性を引数の値に変更した新しい純粋パスオブジェクトを返します。元のパスにに stem 属性がない場合は ValueError が送出されます:
>>> p = PurePath('foo/bar/baz.txt')
>>> p.with_name('hoge.py')
PurePosixPath('foo/bar/hoge.py')
>>> p.with_stem('fuga')
PurePosixPath('foo/bar/fuga.txt')
>>> p = PurePath('foo/bar/piyo.tar.gz')
>>> p.with_stem('fuga')
PurePosixPath('foo/bar/fuga.gz')
with_suffix()は、suffix属性を引数の値に変更した新しい純粋パスオブジェクトを返します。引数が空文字列の場合、suffix属性が削除されます。元のパスにsuffix属性が無かった場合は、引数の値が追加されます。
>>> p = PurePath('foo/bar/piyo.tar.gz')
>>> p.with_suffix('.bz2')
PurePosixPath('foo/bar/piyo.tar.bz2')
>>> p.with_suffix('')
PurePosixPath('foo/bar/piyo.tar')
>>> p = PurePath('comp')
>>> p.with_suffix('.tar')
PurePosixPath('comp.tar')
具象パスクラスのメソッド
具象パスクラスは純粋パスのクラスを継承しているので、純粋パスの機能に加えて、さらに具象パスの特有のメソッドが追加されています。これらのメソッドの多くはシステムコールが失敗するとOSErrorを送出します。ここでは具象パス特有のメソッドについて解説していきます。
ディレクトリの場所を確認する — Path.cwd()、Path.home()
クラスメソッドPath.cwd()は、カレントディレクトリを表すパスオブジェクトを返します。
# Windowsで実行した場合
>>> Path.cwd()
WindowsPath('C:/Users/taro/testdir')
# Windows以外で実行した場合
>>> Path.cwd()
PosixPath('/home/taro/testdir')
クラスメソッドPath.home()は、ユーザーのホームディレクトリを表すパスオブジェクトを返します。ホームディレクトリが解決できない場合はRuntimeErrorを送出します。
# Windowsで実行した場合
>>> Path.home()
WindowsPath('C:/Users/taro')
# Windows以外で実行した場合
>>> Path.home()
PosixPath('/home/taro')
ファイルを一覧する — iterdir()、glob()
ディレクトリのファイルをすべて一覧する
ディレクトリのファイルを一覧するにはiterdir()を使います。iterdir()はパスが示すディレクトリの中のファイルを順に返すジェネレーターオブジェクトを戻します。
>>> p = Path('.')
>>> for file in p.iterdir():
... file
...
PosixPath('directory1')
PosixPath('sample.txt')
PosixPath('directory2')
ファイルは任意の順序で返されます。そして、これには特別なエントリの「.」や「..」は含まれません。
パターンにマッチするファイルを一覧する
glob()メソッドの引数には、パスが示すディレクトリに対する相対パターン(グロブ形式)を指定して呼び出します。すると、マッチするるファイルを順に生成するジェネレーターオブジェクトを返します。
>>> p = Path('.')
>>> list(p.glob('file*'))
[PosixPath('file1'), PosixPath('file2'), PosixPath('file3')]
グロブ形式のパターンで使えるワイルドカードについては次の記事を参照ください。
ファイルの種類を確認する — is_file()、is_dir()、is_symlink()
具象パスのオブジェクトは、ファイルの種類を確認するメソッドも数多く提供しています。ここでは、よく利用する「通常ファイル」、「ディレクトリ」、「シンボリックリンク」を確認するメソッドを紹介します。そのほかの種類を確認するメソッドはドキュメントを参照してください。
次のようなファイルを用意してこれらのメソッドの動作を確認してみましょう。
$ ls -l
合計 0
drwxr-xr-x. 2 taro taro 6 9月 20 13:38 dir1
-rw-r--r--. 1 taro taro 0 9月 20 13:38 foo.txt
lrwxrwxrwx. 1 taro taro 4 9月 7 08:44 linkdir -> dir1
lrwxrwxrwx. 1 taro taro 7 9月 7 08:45 linkfile -> foo.txt
is_file()メソッドは、パスが指すファイルが存在し、かつ通常ファイル(または通常ファイルへのシンボリックリンク)であればTrueを返します。それ以外のFalseを返します。
>>> dp = Path('dir1')
>>> fp = Path('foo.txt')
>>> dp_link = Path('linkdir')
>>> fp_link = Path('linkfile')
>>> fp.is_file()
True
>>> fp_link.is_file()
True
>>> dp.is_file()
False
>>> dp_link.is_file()
False
is_dir()メソッドは、パスが指すディレクトリが存在し、かつディレクトリ(またはディレクトリへのシンボリックリンク)であればTrueを返し、それ以外はFalseを返します。
>>> dp.is_dir()
True
>>> dp_link.is_dir()
True
>>> fp.is_dir()
False
>>> fp_link.is_dir()
False
is_symlink()メソッドは、パスが指すファイルが存在し、かつシンボリックリンクである場合はTrueを返し、それ以外はFalseを返します。
>>> dp_link.is_symlink()
True
>>> fp_link.is_symlink()
True
>>> dp.is_symlink()
False
>>> fp.is_symlink()
False
これらのメソッドは、ファイルを参照する権限がない場合などにエラーを送出します。
ファイルの存在を確認する — exists()
exists()メソッドは、パスが示すファイルが存在する場合はTrueを返します。そうでなければFalseを返します。
パスがシンボリックリンクを指している場合、シンボリックリンクが指しているファイルまたはディレクトリが存在しているかどうかの結果を返します。
>>> Path('/home/taro/testdir/foo.txt').exists()
True
>>> Path('nonexist.txt').exists()
False
>>> Path('.').exists()
True
ファイル属性を確認する — stat()、lstat()、owner()、group()
ファイル属性を一覧する
stat()メソッドは、(os.stat() と同様の) パスが示すファイルの情報を含む os.stat_result オブジェクトを返します。
# Windowsで実行した場合
>>> p = Path('foo.txt')
>>> p.stat()
os.stat_result(st_mode=33060, st_ino=15762598695797871, st_dev=3730806949, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1693869262, st_mtime=1693869262, st_ctime=1693869262)
# Unix系OSで実行した場合
>>> p = Path('foo.txt')
>>> p.stat()
os.stat_result(st_mode=33188, st_ino=1676600, st_dev=64768, st_nlink=1, st_uid=1000, st_gid=1000, st_size=0, st_atime=1693869156, st_mtime=1693869156, st_ctime=1693869156)
os.stat_result オブジェクトが含む情報は属性として参照できます。
>>> s.st_mode
33188
stat()は通常はシンボリックリンクを辿ります。シンボリックリンクのファイル自体の情報を参照したい場合は follow_symlinks=False とするか lstat() を使用します。
lstat()メソッドはstat()と同様ですが、パスがシンボリックリンクを指していた場合、リンク先ではなくシンボリックリンク自身の情報を返します。
>>> link_p = Path('linkfile')
>>> link_p.is_symlink()
True
>>> link_p.lstat()
os.stat_result(st_mode=41471, st_ino=1568074, st_dev=64768, st_nlink=1, st_uid=1000, st_gid=1000, st_size=7, st_atime=1694043915, st_mtime=1694043912, st_ctime=1694043912)
ファイルのアクセス権(パーミッション)の表示
stat_resultオブジェクトのst_mode属性はファイルのアクセス権の情報を含みまが、そのままでは分かりづらいです。これをlsコマンドで表示されるような形式で表示することもできます。それにはstatモジュールのfilemode()を使います。なお、ここでの例はUnix系OSで実行しています。
>>> mode = p.stat().st_mode
>>> mode
33188
>>> import stat
>>> stat.filemode(mode)
'-rw-r--r--'
アクセス権を8進数で表示したい場合には次のようにします。
>>> oct(stat.S_IMODE(mode))
'0o644'
stat.S_IMODE()メソッドはファイルのアクセス権(ファイルの許可ビット)、スティッキービット、セットユーザーID(setuid)ビット、セットグループID(setgid)ビットを数値で返します。これは10進数形式なのでoct()関数で8真数形式の文字列へ変換しています。
試しにスティッキービットが設定されている/tmpディレクトリも同じように表示してみます。
>>> tmp_p = Path('/tmp')
>>> tmp_mode = tmp_p.stat().st_mode
>>> stat.filemode(tmp_mode)
'drwxrwxrwt'
>>> oct(stat.S_IMODE(tmp_mode))
'0o1777'
ファイルの所有者とグループ所有者を確認する
owner()はファイルの所有者のユーザー名を返し、group()はファイルを所有するグループ名を返します。
>>> p.owner()
'taro'
>>> p.group()
'taro'
ファイルのアクセス権(モード)を変更する — chmod()
chmod()メソッドは、os.chmod() のようにファイルのアクセス権限(モード)を変更します。通常、このメソッドはシンボリックリンクを辿ります。
>>> p = Path('foo.txt')
>>> stat.filemode(p.stat().st_mode)
'-rw-r--r--'
>>> p.chmod(0o755)
>>> stat.filemode(p.stat().st_mode)
'-rwxr-xr-x'
ディレクトリの作成と削除 — mkdir()、rmdir()
パスが示すディレクトリを作成したり削除するメソッドはmkdir()とrmdir()です。
ディレクトリの作成
ディレクトリを作成するにはmkdir()を使います。
>>> d1 = Path('directory1')
>>> d1.mkdir()
>>> d1.is_dir()
True
ディレクトリを作成するときにアクセス権(モード)を指定することもできます。
>>> d2 = Path('directory2')
>>> d2.mkdir(mode=0o666)
>>> stat.filemode(d2.stat().st_mode)
'drw-r--r--'
>>> stat.filemode(d1.stat().st_mode)
'drwxr-xr-x'
アクセス権(モード)はプロセスのumask値と組み合わせて決定されます。この例のumask値は「0022」なので、directory2のアクセス権は「0o644」になります。mode引数のデフォルト値は「0o777」ですので、directory1ディレクトリのアクセス権は「0o755」です。
ディレクトリが既に存在した場合はFileExistsErrorを送出します。
>>> d1.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: 'directory1'
存在しないパスであればFileNotFoundErrorエラーを送出します。次の例ではdirectory3が存在しないためエラーになります。
>>> subd = Path('directory3/subdir')
>>> subd.mkdir()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1323, in mkdir
self._accessor.mkdir(self, mode)
FileNotFoundError: [Errno 2] No such file or directory: 'directory3/subdir'
引数に「parents=True」を指定すれば、存在しない親ディレクトリも作成することができます。
>>> subd.mkdir(mode=0o666, parents=True)
>>> stat.filemode(subd.stat().st_mode)
'drw-r--r--'
>>> stat.filemode(Path('directory3').stat().st_mode)
'drwxr-xr-x'
親ディレクトリのアクセス権はmode引数の値ではなくデフォルト値(0o777)にしたがって決定されます。
ディレクトリの削除
ディレクトリを削除するにはrmdir()メソッドを使います。ディレクトリは空である必要があります。
>>> d2.rmdir()
>>> d2.exists()
False
ファイルの作成と削除 — touch()、unlink()
パスが示すファイルを作成するにはtouch()、削除するにはunlink()を使います。
ファイルを作成する
ファイルを作成するにはtouch()メソッドを使います。
>>> fp1 = Path('file1')
>>> fp1.touch()
>>> fp1.is_file()
True
ファイルを作成するときにアクセス権(モード)を指定することもできます。
>>> fp2 = Path('file2')
>>> fp2.touch(mode=0o777)
>>> stat.filemode(fp2.stat().st_mode)
'-rwxr-xr-x'
>>> stat.filemode(fp1.stat().st_mode)
'-rw-r--r--'
アクセス権(モード)はプロセスのumask値と組み合わせて決定されます。この例のumask値は「0022」なので、file2のアクセス権は「0o755」になります。mode引数のデフォルト値は「0o666」なので、file1のアクセス権は「0o644」になります。
デフォルトではファイルが既に存在してもエラーにはならず、ファイルのタイムスタンプだけが現在の日時に更新されます。
>>> fp3.stat()
os.stat_result(st_mode=33261, st_ino=1440282, st_dev=64768, st_nlink=1, st_uid=1000, st_gid=1000, st_size=9, st_atime=1695193768, st_mtime=1695193766, st_ctime=1695193766)
>>> oct(stat.S_IMODE(fp3.stat().st_mode))
'0o755'
>>> fp3.touch(mode=0o666)
>>> fp3.stat()
os.stat_result(st_mode=33261, st_ino=1440282, st_dev=64768, st_nlink=1, st_uid=1000, st_gid=1000, st_size=9, st_atime=1695194022, st_mtime=1695194022, st_ctime=1695194022)
mode引数を指定しても無視され、タイムスタンプ(st_atime、st_mtime、st_ctime)の値だけが更新されていることがわかります。
引数exist_okにFalse(デフォルトはTrue)を指定すると、ファイルが存在した場合にFileExistsErrorを送出します。
>>> fp3.touch(exist_ok=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1315, in touch
fd = self._raw_open(flags, mode)
File "/usr/lib64/python3.9/pathlib.py", line 1127, in _raw_open
return self._accessor.open(self, flags, mode)
FileExistsError: [Errno 17] File exists: 'file3'
ファイルを削除する
ファイルを削除するにはunlink()メソッドを使います。このメソッドはファイルまたはシンボリックリンクを削除します。
>>> fp1
PosixPath('file1')
>>> fp1.unlink()
>>> fp1.exists()
False
パスが示すファイルが存在しない場合はFileNotFoundErrorを送出します。
>>> fp1.exists()
False
>>> fp1.unlink()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.9/pathlib.py", line 1354, in unlink
self._accessor.unlink(self)
FileNotFoundError: [Errno 2] No such file or directory: 'file1'
missing_ok引数にTrue(デフォルトはFalse)を指定すると、ファイルが存在しない場合でもエラーになりません。
>>> fp1.exists()
False
>>> fp1.unlink(missing_ok=True)
名前の変更 — rename()、replace()
rename()メソッドは、パスが示すファイルの名前を引数に渡されたパスに変更し、変更後のパスを表す新しいパスオブジェクを返します。引数のパスは文字列かパスオブジェクを指定できます。
>>> p = Path('foo.txt')
>>> p.exists()
True
>>> p2 = p.rename('bar.txt')
>>> p2
PosixPath('bar.txt')
>>> p.exists()
False
>>> p2.exists()
True
>>> target = Path('dir1/baz.txt')
>>> p3 = p2.rename(target)
>>> p3
PosixPath('dir1/baz.txt')
>>> p2.exists()
False
>>> p3.exists()
True
replace()メソッドも、rename()メソッドと同様に、パスが示すファイルの名前を引数に渡されたパスに変更し、変更後のパスを表す新しいパスオブジェクを返します。ただし、replace()メソッドは変更後のパスがファイルか空のディレクトリを指していた場合、無条件にこれを置き換えます。
次の例では空のディレクトリを置き換えています。
>>> dir1 = Path('dir1')
>>> list(dir1.iterdir())
[PosixPath('dir1/foo1.txt'), PosixPath('dir1/foo2.txt'), PosixPath('dir1/baz.txt')]
>>> target_dir = Path('empty_dir')
>>> list(target_dir.iterdir())
[]
>>> new = dir1.replace(target_dir)
>>> new
PosixPath('empty_dir')
>>> list(new.iterdir())
[PosixPath('empty_dir/foo1.txt'), PosixPath('empty_dir/foo2.txt'), PosixPath('empty_dir/baz.txt')]
パスを解決する — expanduser()、abusolute()、resolve()
ホームディレクトリを解決する
expanduser()は、パス要素の先頭の「~」または「~user」をユーザーのホームディレクトリのパスに置き換えた新しいパスオブジェクトを返します。ホームディレクトリが解決できない場合は RuntimeError を送出します。
>>> Path('~/testdir/nonexist.txt').expanduser()
PosixPath('/home/taro/testdir/nonexist.txt')
絶対パスを取得する
abusolute()メソッドは、パスの正規化やシンボリックリンク解決を行わずに、パスを絶対パスにした新しいパスオブジェクトを返します。
>>> p = Path('sample.txt')
>>> p.absolute()
PosixPath('/home/taro/testdir/sample.txt')
resolve()メソッドもパスを絶対パスにした新しいパスオブジェクトを返しますが、シンボリックリンクなどを解決します。
>>> Path('.').resolve()
PosixPath('/home/taro/testdir')
ファイル入出力
具象パスクラスでは、ファイル入出力のためのメソッドも提供しています。
ファイルをオープンする — open()
具象パスクラスのopen()メソッドは、組み込みのopen()関数と同様に、パスが指しているファイルをオープンします。ファイルのオープンに成功するとファイルオブジェクトを返します。
引数についても組み込みのopen()関数と同様、次のものが指定できます。
- mode引数
- newline引数
- encoding引数
mode引数を位置引数として指定する場合は、組み込みのopen()関数と異なり1番目の引数として指定します。また、mode引数を省略した場合、テキストモードかつ読み取り専用で開かれるのは組み込みのopen()関数と同じです。
具象パスクラスのopen()メソッドもファイルオブジェクトを返すので、ファイルをオープンした後はファイルオブジェクトのメソッドを使って読み書きします。ファイルを使い終えたら速やかにファイルオブジェクトのclose()メソッドでファイルをクローズするか、with文で自動的にクローズすべきであることも同様です。
次に具象パスのオブジェクトを使ってファイルをオープンする例を示します。
>>> text = '''line1
... line2
... '''
>>> p = Path('sample.txt')
>>> with p.open('w') as fout:
... fout.write(text)
...
12
>>> with p.open() as fin:
... contents = fin.read()
... print(contents)
...
line1
line2
引数の意味や使い方、ファイルを読み書きするためのファイルオブジェクトのメソッドについては次の記事で詳しく説明しているのでご参照ください。
ファイルに書き込む — write_text()、write_bytes()
write_text()メソッドを使うと、テキストを簡単にファイルへ書き込むことができます。このメソッドはテキストモードでファイルを開き、テキストを書き込み、ファイルを閉じるという一連の動作を実施します。
書き込むテキストはメソッドの最初の引数に指定します。
>>> p = Path('text.txt')
>>> p.write_text('line1\nline2\n')
12
>>> p.read_text()
'line1\nline2\n'
このメソッドにnはewline引数やencoding引数を指定することもできます。これら引数の使い方は組み込みのopen()関数と同じです。
バイナリデータを書き込むwrite_bytes()メソッドもあります。
>>> p = Path('binary_file')
>>> p.write_bytes(b'binary contents')
15
>>> p.read_bytes()
b'binary contents'
ファイルを読み込む — read_text()、read_bytes()
read_text()メソッドを使うと、テキストファイルを簡単に読み込むことができます。このメソッドはテキストモードでファイルを開き、テキストを読み込み、ファイルを閉じるという一連の動作を実施します。
>>> p = Path('text.txt')
>>> p.read_text()
'line1\nline2\n'
このメソッドにはencoding引数を指定することはできますが、newline引数は利用できません。
バイナリデータを読み込むread_bytes()メソッドもあります。
>>> p = Path('binary_file')
>>> p.read_bytes()
b'binary contents'
スラッシュ(/)演算子
スラッシュ(/)演算子は、純粋パスのjoinpath()のようにパスを結合します。これはスラッシュ(/)演算子のオペランドがパスオブジェクトだった場合、結合を実行するようにスラッシュ演算子がオーバーライドされているからです。
>>> PurePosixPath('/home') / 'taro'
PurePosixPath('/home/taro')
>>> '/home' / Path('taro') / 'sample.txt'
PosixPath('/home/taro/sample.txt')
>>> PureWindowsPath('c:') / '/taro'
PureWindowsPath('c:/taro')
まとめ
pathlibモジュールは、今まで幾つかのモジュールに分散されていた機能が統合されていて、パスやファイルに関する処理は、このモジュールだけで十分な場合が多いでしょう。これからはos.pathモジュールを使うより、pathlibモジュールに慣れたほが生産性が上がると思います。