この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。DA事業本部の春田です。
今さらですが、最近Pythonの Pathlib という標準ライブラリを認知しました。
いやはや、めちゃめちゃ良いですねコレ。すでにご存知の方も多いかと思いますが、そのエレガントさに身を任せてご紹介していきます。
- 実行環境:
- macOS Mojave 10.14.6
- Python 3.7.5
※PathlibはPython3.4以降で導入されています
基本
こんな構成のディレクトリ群を例とします。
animals <- I'm here!
├── dogs
│ ├── shiba.py
│ ├── beagle.txt
│ └── pug
│ └── buchakawa.json
└── cats
│ ├── scottish.txt
│ └── doraemon.exe
└── hamaguchi.py
PathlibはPython3.4以降の標準ライブラリなので、シュパッと import
できます。まずはメインクラスの Path
を入れます。
>>> from pathlib import Path
Path
の引数にパスを含んだ文字列を渡すと、 PosixPath
インスタンスが返されます。(実行環境がWindowsの場合は WindowsPath
ですかね?)このインスタンスで、あんなことやこんなことができるわけですね。存在確認は exists()
メソッドを用います。
>>> dogs = Path('./dogs')
>>> dogs
PosixPath('dogs')
>>> print(dogs)
dogs
>>> dogs.exists()
True
Pathlibの最もエレガントな仕様は、パスを除算記号 /
で繋げられる点だと思います。文字列を format
したり +
で繋げる方法よりも、記述量が減り読みやすくなります。要素の一部に PosixPath
インスタンスが使われていれば、文字列でも繋げられます。
>>> shiba = dogs / 'shiba.py'
>>> shiba
PosixPath('dogs/shiba.py')
>>> shiba.exists()
True
shiba
から buchakawa.json
を指したいな、という時には、 parent
もしくは parents
プロパティの出番です。
>>> bucha1 = shiba.parent / 'pug' / 'buchakawa.json'
>>> bucha1
PosixPath('dogs/pug/buchakawa.json')
>>> bucha2 = shiba.parents[0] / 'pug' / 'buchakawa.json' # スライスで親ディレクトリの位置を指定
>>> bucha2
PosixPath('dogs/pug/buchakawa.json')
ファイル名を取りたい場合は name
、拡張子が欲しい時は suffix
、拡張子を除いたファイル名が欲しい場合は stem
といったプロパティも用意されています。本当にプロパティの種類が豊富ですね。
>>> bucha.name
'buchakawa.json'
>>> bucha.suffix
'.json'
>>> bucha.stem
'buchakawa'
応用
検索の glob
メソッドではジェネレータ生成され、マッチしたPosixPathインスタンスが返されます。
>>> cwd = Path('.')
>>> cwd.glob('*')
<generator object Path.glob at 0x103e5cad0>
>>> [*cwd.glob('*')]
[PosixPath('dogs'), PosixPath('cats'), PosixPath('hamaguchi.py')]
>>> [*cwd.glob('cats/*.exe')]
[PosixPath('cats/doraemon.exe')]
Pathに任意の文字列が含まれているかどうか判定するには、 match
メソッドを使います。右側から一致を調べていく仕様なので、それに合わせたパスも記述する必要があります。
>>> bucha
PosixPath('dogs/pug/buchakawa.json')
>>> bucha.match('pug')
False
>>> bucha.match('*/pug')
False
>>> bucha.match('pug/*')
True
僕のお気に入りは relative_to
メソッドです。起点のパスからの相対パスを表現することができます。これを応用すると、任意のファイルに対してディレクトリの付替えなどができます。
>>> bucha
PosixPath('dogs/pug/buchakawa.json')
>>> pug = Path('dogs/pug')
>>> pug
PosixPath('dogs/pug')
>>> scottish = Path('cats/scottish.txt')
>>> scottish
PosixPath('cats/scottish.txt')
>>> scottish.parent / bucha.relative_to(pug)
PosixPath('cats/buchakawa.json')
ファイル名や拡張子を付け替えたい時は、 with_name
や with_suffix
メソッドが使えます。うわぁぁ!拡張子だけ変えたいのにっっ!!なんてことよくありませんか?
>>> bucha.with_name('mechakawa.yaml')
PosixPath('dogs/pug/mechakawa.yaml')
>>> scottish.with_suffix('.json')
PosixPath('cats/scottish.json')
>>> scottish.with_suffix('json') # '.'が必要です
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 843, in with_suffix
raise ValueError("Invalid suffix %r" % (suffix))
ValueError: Invalid suffix 'json'
その他、スクリプト内でファイルシステムに触れるのはちょっと怖いな、という時は Path
クラスの代わりに PurePath
クラスを使用すると機能が制限されます。
>>> from pathlib import PurePath
>>> shiba = PurePath('dogs/shiba.py')
>>> shiba.name
'shiba.py'
>>> shiba.exists()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'PurePosixPath' object has no attribute 'exists'
>>> shiba.glob('../')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'PurePosixPath' object has no attribute 'glob'
凄いシンプルなライブラリですが、組み合わせ次第で可能性は無限大!!という感じですね。ただし、Pathクラスは文字列ではないので、PathLikeオブジェクトに対応していない関数やメソッドでは、一旦 str()
などを噛ませる必要があります。その点だけちょっと面倒ですね。。
最後に
ご紹介しきれなかった機能もあるので、興味のある方は公式ドキュメントもご参照ください!