皆さんはPythonのpathlib使ってますか?私は非常によく使っています.例えば機械学習では学習の前に前処理を多く行うケースが非常に多いですが,このような時にpathlibを知っておくと便利です.pathlib
は意外とPython 3.4(2014年~)からとそれなりに新しいため,古くからのPythonユーザーは os.path
の方をよく使っているかもしれません.ただpathlib
は文字列ではなくPath
クラスとして扱ってくれることで,例えばLinux/Windowsのパス表記の違いを吸収してくれたりします.
pathlib
とos.path
の比較は公式のpathlib
ドキュメントに譲るとして,私がよく使うpathlib
のクラスを紹介します.また今回改めてドキュメントを眺めて知った便利関数も多いので,公式ドキュメントに目を通すのもオススメします.
今回は下記のような複数の素性の異なるデータセットに対して処理するケースなどを考えてみます.
. ├── datasetA │ ├── abc │ │ ├── a.wav │ │ └── b.wav │ └── def │ └── d.wav └── datasetB ├── 123.wav └── sub └── 456.wav
便利関数26選
検索・走査系
この時に使えるのが,glob
あるいはrglob
です.個人的にはrglob
の方がショートカット記法になっていて,正規表現のタイポが避けられるので好んで使っています.スペースの都合上 list
化して表示しています.
from pathlib import Path In [1]: list(Path(".").rglob("*.wav")) Out[1]: [PosixPath('datasetA/abc/a.wav'), PosixPath('datasetA/abc/b.wav'), PosixPath('datasetA/def/d.wav'), PosixPath('datasetB/123.wav'), PosixPath('datasetB/sub/456.wav')] In [2]: list(Path(".").glob("**/*.wav")) Out[2]: [PosixPath('datasetA/abc/a.wav'), PosixPath('datasetA/abc/b.wav'), PosixPath('datasetA/def/d.wav'), PosixPath('datasetB/123.wav'), PosixPath('datasetB/sub/456.wav')]
サブディレクトリをリストアップしたい
os.listdir
相当の捜査です.
In [3]: list(Path(".").iterdir()) Out[3]: [PosixPath('datasetA'), PosixPath('datasetB')]
編集系
例えばデータセット内のディレクトリ構造を維持したまま,新たに別のディレクトリにしてパスをすげ替えてファイルを書き出す場合などに有用です.
In [4]: Path("/home/jojonki/datasetA/abc.wav").relative_to("/home/jojonki") Out[4]: PosixPath('datasetA/abc.wav')
ファイル名や拡張子を変更したい
これもよく使います.
# ファイル名を変更 In [5]: Path("datasetA/abc.wav").with_name("AAA.mp3") Out[5]: PosixPath('datasetA/AAA.mp3') # 拡張子を除くファイル名を変更 In [6]: Path("datasetA/abc.wav").with_stem("AAA") Out[6]: PosixPath('datasetA/AAA.wav') # 拡張子を変更 In [7]: Path("datasetA/abc.wav").with_suffix(".mp3") Out[7]: PosixPath('datasetA/abc.mp3') # os.rename相当 In [8]: Path("datasetA/abc/b.wav").rename("datasetA/abc/c.wav") Out[8]: PosixPath('datasetA/abc/c.wav')
宣言系
Pathインスタンスを生成したい
非常に直感的にPathクラスを作れます.例えば何らかのデータのディレクトリに対して動的にパスを作る際などに便利です.
# スラッシュでつながる In [9]: root_dir = Path("datasetA/abc") In [9]: root_dir / "sub" / "def.wav" Out[9]: PosixPath('datasetA/abc/sub/def.wav') # os.path.joinと同様 In [10]: Path("datasetA").joinpath("abc", "def.wav") Out[10]: PosixPath('datasetA/abc/def.wav')
生成系
ディレクトリを生成したい
In [11]: Path("datasetC/").mkdir() # 必要に応じて親ディレクトリも作成.mkdir -p, os.makedirs相当 In [12]: Path("datasetD/1/2/3/4/5").mkdir(parents=True)
ファイルを読み書きしたい
これは知りませんでしたが便利そうです.
In [13]: Path("README.txt").write_text("# Title\nhello") Out[13]: 13 In [14]: Path("README.txt").read_text() Out[14]: '# Title\nhello'
アクセス系
これらもよく使うので頭の片隅に入れておくと良いと思います.
パスの存在を確認したり,絶対パスを取得したい
# 存在確認 In [15]: Path("datasetA").exists() Out[15]: True # ディレクトリ? In [16]: Path("datasetA").is_dir() Out[16]: True # 絶対パス? In [17]: Path("datasetA").is_absolute() Out[17]: False # 絶対パス取得 In [18]: Path("datasetA").absolute() Out[18]: PosixPath('/Users/jonki/sandbox/datasetA')
ファイル名に関しての色々な情報を取りたい
In [19]: Path("datasetA/abc/a.wav") Out[19]: PosixPath('datasetA/abc/a.wav') # 拡張子も含めて In [20]: Path("datasetA/abc/a.wav").name Out[20]: 'a.wav' # 拡張子除く In [21]: Path("datasetA/abc/a.wav").stem Out[21]: 'a' # 拡張子を取得 In [22]: Path("datasetA/abc/a.wav").suffix Out[22]: '.wav' # 最後の.gzだけ In [23]: Path("data.tar.gz").suffix Out[23]: '.gz' # まとめて拡張子を取る In [24]: Path("data.tar.gz").suffixes Out[24]: ['.tar', '.gz'] # そのファイル/ディレクトリの親ディレクトリ In [25]: Path("datasetA/abc/a.wav").parent Out[25]: PosixPath('datasetA/abc') # パスを分解 In [26]: Path("datasetA/abc/a.wav").parts Out[26]: ('datasetA', 'abc', 'a.wav')
まとめ
以上Pythonのpathlibをまとめました.
昨今はChatGPTなどでやりたいことを伝えればChatGPTが生成してくれますが,今回紹介したようなコードは非常によく使うので覚えておいて損はないと思います.文字列同士を無理やり駆使してファイル名を変更していたりするコードがたまにありますが,pathlib
を使えばそのようなこともなくなりますね.