はじめに
タイトルの通りです。
今まで os モジュールの os.path を利用してファイルやディレクトリなどの操作をしていて不自由は感じていませんでした。が、今回、Windowsで動かしている方のコードを動かそうとしたとき、ファイルが "¥hooo\aaa.txt"みたいに書かれていて、自分のlinux 環境では動かせませんでした。そこで、どちらでも使えるはずのpathlib.Path を調べてみよう、ということになりました。
動作確認
ファイルやディレクトリの参照
今までstrとして扱っていたファイルやディレクトリのパスの情報
p:str = "./a/b"
を
from pathlib import Path
p = Path('.','a','b')
とします。
from pathlib import Path
p = Path(__file__).parent
print('p=', p, ' type(p)=', type(p))
print('p.name=', p.name)
print('p.is_dir=', p.is_dir(), ' p.is_file=', p.is_file())
print('---')
p2 = p/Path('hoge')/Path('foo')
p2 = Path('hoge','foo')
print('p2=', str(p2))
print('p2.name=', p2.name)
print('p2.parts=', p2.parts)
print('p2.exsits=', p2.exists())
を実行すると下記の出力を得ました。
$ python sample.py
p= /home/x77/my_tips/testdir type(p)= <class 'pathlib.PosixPath'>
p.name= testdir
p.is_dir= True p.is_file= False
---
p2= hoge/foo
p2.name= foo
p2.parts= ('hoge', 'foo')
p2.exsits= False
ディレクトリ内のファイル
今のフォルダ構成。
$ tree .
.
├── a
│ └── b
│ ├── c
│ │ ├── 001
│ │ ├── 002
│ │ └── 003
│ └── xyz.py
└── sample.py
で、以下を実行してみます。
from pathlib import Path
p = Path(__file__).parent
print('p.iterdir()')
for child in p.iterdir():
print(child)
print('')
print('glob("**/*.py")')
for f in p.glob("**/*.py"):
print(f)
print('')
print('glob("**/*")')
for f in p.glob("**/*"):
print(f)
出力は下記の通り。itedir()では直下のディレクトリにあるファイルとディレクトリを取得できるが、再帰的に下には下りて行かない。glob を使えば、拡張子でフィルタをかけて、再帰的に取得できました。
$ python sample2.py
p.iterdir()
/home/x77/my_tips/testdir/a
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py
glob("**/*.py")
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py
/home/x77/my_tips/testdir/a/b/xyz.py
glob("**/*")
/home/x77/my_tips/testdir/a
/home/x77/my_tips/testdir/sample2.py
/home/x77/my_tips/testdir/sample.py
/home/x77/my_tips/testdir/a/b
/home/x77/my_tips/testdir/a/b/xyz.py
/home/x77/my_tips/testdir/a/b/c
/home/x77/my_tips/testdir/a/b/c/001
/home/x77/my_tips/testdir/a/b/c/003
/home/x77/my_tips/testdir/a/b/c/002
CUIコマンドの引数として
python で引数の解析に argparse を利用することがあるそうです。このとき、ディレクトリ名を引数にするとき、type に Path を利用するのが良さそうです。
何故ファイルのパスには使うと言わないのかというと、それはargparse にすでに argparse.FileType というオブジェクトがあるからです。が、自分は使い方が分かっていないので、Path を使っていたりします。下記が解説ですが。
os のモジュールとの対応付け
ドキュメントを読むと分かるのですが、WindowsとLinux/Mac(Posix準拠)の相互変換もサポートしながら、全体としてPathの参照の違いを吸収できるように設計されています。さらに、socket や mount point など、OSと関係するところもサポートしてくれています。
一方それらの機能は、os モジュールでサポートもされています。対応表は下記で参照できます。
まとめ
雑感ですが、世の中os モジュールですませている人もまだまだ多く、Linux/Macで完結している場合どうしてもPath を使うというインセンティブが働かないだろうと思いました。(自分がそうだった。)いろいろな機能がありますが、必要に応じてこちらで書いていこうと思います。
とりあえず pathlib.Path について必要だったことをメモっておきました。今週も何とか生き延びることができました。神様皆様ありがとうございます。
(2023/11/11)