Python、pathlibモジュールを使う

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - 標準ライブラリ
2018年11月22日21:25に更新(約21日前)
2018年11月8日16:27に作成(約35日前)

旧ブログ移行記事です。

pathlibについて

公式ドキュメントから抜粋

このモジュールはファイルシステムのパスを表すクラスを提供していて、様々なオペレーティングシステムについての適切な意味論をそれらのクラスに持たせています。 Path クラスは 純粋パス と 具象パス からなります。 純粋パスは I/O を伴わない純粋な計算操作を提供します。 具象パスは純粋パスを継承していますが、 I/O 操作も提供しています。

pathlibはPython3.4で追加されたモジュールで、パス操作に関するモジュールです。

今まで、Pythonでパスを扱うには、os.pathモジュールを使うのが一般的でした。実際のところos.pathモジュールは大変便利で、不満なく使っている方も多いはずです。私もそうでした。

pathlibモジュールではパスを表すクラスを提供していて、便利な属性へのアクセスや操作ができるようになり、更に直感的な操作が可能にもなっています。

大雑把な使い方としては、Path()のようにインスタンス化すると、Pathオブジェクトというものが返されます。このPathオブジェクトに対して、色々と操作をしていくのが基本となります。

from pathlib import Path

path = Path()

Path()のように引数を空にすると、カレントディレクトリのPathオブジェクトを作成します。もちろん、相対パス・絶対パスを引数として指定することもできます。

from pathlib import Path

path = Path(r'C:\Users\torit\src')

上はWindowsのパスを指定していますが、WindowsならWindowsの、Unix系ならUnix系のPathオブジェクトを内部で作成してくれていますので、こうしたプラットフォームの違いを意識する必要はありません。

Pathオブジェクトについて

PathオブジェクトPathを表現するオブジェクトです。自身が指しているパスの情報に加え、パス操作に関する便利な属性・メソッドを持っています。

メソッドの中には新たなパスオブジェクトを返すものもあります。例えば、そのパス内のファイル・ディレクトリ一覧を取得するにはPathオブジェクト.iterdir()としますが、これは中のファイル・ディレクトリ1つ1つをPathオブジェクトとして返します。これにより、再帰的な呼び出しであったり、統一的な処理が行えるようになっています。

Path()の引数とパスの結合

上では引数を1つ、相対パスや絶対パスとして渡しました。

path = Path(r'C:\Users\torit\src')

複数の文字列を渡してパスを作ることもできます。

path = Path('src', 'python', 'naritoblog')  # src/python/naritoblog

また、別のPathオブジェクトを引数にすることもできますし、文字列との混合も可能です。パスの結合もこれで行えます。

path1 = Path('src')
path2 = Path('python')
path3 = Path(path1, path2, 'naritoblog')  # src/python/naritoblog

更に、Pathオブジェクトは/演算子に対応しています。上のコードは、以下のように書き換えることができます。パスの結合ならば、こちらのほうが直感的かもしれませんね。

path1 = Path('src')
path2 = Path('python')
path3 = path1 / path2 / 'naritoblog'  # src/python/naritoblog

操作感としては、os.path.joinと似たようなもので、それに置き換えて使用できます。

文字列としてのパスが欲しい

Pathオブジェクトではなく、パスの文字列がどうしても欲しい場合は組み込み関数strに渡すことができます。

from pathlib import Path

path = Path()
str_path = str(path)  # これで文字列のパスが取得できる。

状況によりますが、Pathオブジェクト自体は非常に便利なので、すぐに文字列に変換するよりもPathオブジェクトのまま持っているほうが捗りがちです。Pathオブジェクトの__str__がパスの文字列を返すので、組み込み関数printのように内部的に__str__を呼び出す処理ではそのままPathオブジェクトが使えます。

相対パスを絶対パスに変換したい

Path()に相対パス(空欄でもカレントも含む)を渡した場合は、Pathオブジェクトも相対パスで作成されます。つまり、以下のようなコードの場合は...

from pathlib import Path

path = Path()
for file_or_dir in path.iterdir():
    print(file_or_dir)

以下のように表示されます。

main.py
Pipfile
Pipfile.lock

Pathオブジェクトにはresolveというメソッドがあり、これは相対パスを絶対パスに変換してくれます

from pathlib import Path

path = Path()
for file_or_dir in path.iterdir():
    print(file_or_dir.resolve())

ちゃんと絶対パスが表示されますね。

C:\Users\torit\src\python\cui\scrapytest\main.py
C:\Users\torit\src\python\cui\scrapytest\Pipfile
C:\Users\torit\src\python\cui\scrapytest\Pipfile.lock

ファイル・ディレクトリの一覧取得

上で既に軽く触れましたが、Pathオブジェクトにはiterdirというメソッドがあります。それを呼び出すと、中にあるファイル・ディレクトリの一覧が取得できます。Pathオブジェクトとして返されます。

from pathlib import Path

path = Path(r'C:\Users\torit\src')
for file_or_dir in path.iterdir():
    print(file_or_dir)

結果

C:\Users\torit\src\book
C:\Users\torit\src\c
C:\Users\torit\src\html
C:\Users\torit\src\python
C:\Users\torit\src\tcl_tk
C:\Users\torit\src\udemy

ファイルの一覧だけ欲しい場合

Pathオブジェクトにはis_fileというメソッドがあり、自身がファイルならばTrueを返します。

from pathlib import Path

path = Path(r'C:\Users\torit\src')
for file_or_dir in path.iterdir():
    if file_or_dir.is_file():  # ファイルならば
        print(file_or_dir)

ディレクトリの一覧だけ欲しい場合

同様に、is_dirというメソッドが使えます。

from pathlib import Path

path = Path(r'C:\Users\torit\src')
for file_or_dir in path.iterdir():
    if file_or_dir.is_dir():  # ディレクトリならば
        print(file_or_dir)

再帰的にスキャンする

以下は、カレント以下にある全てのファイルを出力するサンプルです。

from pathlib import Path


def print_all_file(path):
    """再帰的にiterdir()を呼び出し、全てのファイルを出力する。"""
    if path.is_dir():
        for p in path.iterdir():
            print_all_file(p)
    elif path.is_file():
        print(path.resolve())  # わかりやすいよう、絶対パスに


path = Path('')
print_all_file(path)

iterdir()は、os.walk()os.listdir()os.scandir()の代わりに使えそうです。ここから発展して、ディレクトリなどのサイズを取得したい場合Pythonでディレクトリのサイズを一覧表示するも御覧ください。

ファイル・ディレクトリ操作

ファイルの作成

組み込み関数openを使わずとも、Pathオブジェクトにopenめいたものが用意されています。 Pathオブジェクトの引数は実在するパスじゃなきゃダメって訳でもないので、ファイルの新規作成はできます。

from pathlib import Path

a_txt_path = Path('a.txt')
with a_txt_path.open('w', encoding='utf-8') as file:
    file.write('hello')

Pathオブジェクトにはexists()というファイルやディレクトリが存在するかどうかをチェックするメソッドがあるので、これと組み合わせることもよくあります。

ファイルの読み込み

ファイルの内容を読み込むことも、当然できます。

from pathlib import Path

a_txt_path = Path('a.txt')
with a_txt_path.open('r', encoding='utf-8') as file:
    print(file.read())

読み込み・書き込みのショートカット

単純な書き込み、読み込みのために便利なショートカットもあります。write_textread_textですね。

from pathlib import Path

a_txt = Path('a.txt')
a_txt.write_text('hello!!', encoding='utf-8')  # 書き込み!
src = a_txt.read_text(encoding='utf-8')  # 読み込み!
print(src)

ディレクトリの作成

mkdirによるディレクトリ作成もできます。parents=Trueにすると親ディレクトリも一緒に作成され、exist_okは、同名フォルダがあってもOKだよという引数です。

from pathlib import Path

test_dir = Path('test', 'aaa', 'aiueo')  # Path('test') / 'aaa' / 'aiueo' もできる
test_dir.mkdir(parents=True, exist_ok=True)

ファイル・ディレクトリの削除

unlinkで、ファイルの削除ができます。rmdirはディレクトリの削除です。 rmdirは空じゃないと削除できないので、まとめて削除する場合はshutil.rmtreeを使いましょう。

from pathlib import Path

a_txt = Path('a.txt')
a_txt.unlink()

my_dir = Path('test')
my_dir.rmdir()

ファイル・ディレクトリのリネーム

名前の変更はrenameを使います。renameの引数は文字列でもいいし、Pathオブジェクトでもいいです。

from pathlib import Path

a_txt = Path('a.txt')
a_txt.write_text('test', encoding='utf-8')
a_txt.rename('b.txt')

まとめ

まだまだ説明していない機能もあります。公式も興味があれば見てみてください。

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする