【pathlib】Pythonのパス操作: 参照/変更、パスの検索、ファイル操作

title_pathlib TECHNOLOGY

pathlibはPython3.4で標準搭載された、Pythonでファイルやディレクトリのパスを参照、検索、作成などの操作をするライブラリです。
パス操作は特定のタスクに限らず頻繁に使われるので、基本的な使い方を抑えておくことで仕事効率を向上できます。

pathlibの頻出機能

pathlibはPython3.4で標準搭載されているため、利用にあたってインストールの必要はなく、importしてすぐに利用できます。
ここでは、パスの探索/変更及び検索方法が一目でわかるように、一覧でよく使う機能を紹介します。

パスの参照/変更

ファイルバスとして、"/top_dir/data/001.png"を例としてpathlibの機能を説明します。

from pathlib import Path

file_path = Path('/top_dir/data/001.png') # pathlibオブジェクトでパスを取得
dir_path = file_path.parent # ファイルの親ディレクトリのパス(/top_dir/data/)
実行形式結果説明
file_path.name001.pngファイル名を取得
file_path.stem001拡張子を除いたファイル名
file_path.suffix.png拡張子を取得
file_path.with_name(name)/top_dir/data/aaa.jpg
※ name=aaa.jpgとした場合
file_pathからname部分を変更
file_path.with_stem(stem)/top_dir/data/bbb.png
※ stem=bbbとした場合
file_pathからstem部分を更新
file_path.with_suffix(suffix)/top_dir/data/001.json
※ suffix=.jsonとした場合
file_pathからsuffix部分を更新
file_path.parent/top_dir/data親ディレクトリのパスを取得
file_path.parents[num]/top_dir
num=1の場合
numで指定した分の親ディレクトリのパスを取得
parets[0]はparent同じ結果になる。
file_path.relative_to(path)data/001.png
path=/top_dir/とした場合
pathからの相対パスを取得
file_path.resolve()/top_dir/data/001.png
※ Path('001.png').resolve()の実行結果
file_pathを絶対パスへ変換
dir_path.joinpath(name1, name2,…)/top_dir/data/AAA/BBB
※ joinpath('AAA', 'BBB')とした場合
指定したnameを繋げたパスを作成

リファレンス: pathlib --- オブジェクト指向のファイルシステムパス

パスの一覧取得/検索

ここでは以下のディレクトリ構成を例として、パスの一覧取得(iterdir)、検索(glob)の概要を紹介します。

 top_dir 
 ├── config.yaml
 ├── data
 │       ├── 001.png
 │       ├── 002.png
 │       ├── 003.yaml
 │       └── sample.txt
 ├── readme.md
 └── src
           ├── lib
           │   └── utils.py
           └── main.py 
機能説明
Path.iterdir()ディレクトリ直下のパス一覧をイタレータとして取得。
注: Pathはディレクトリである必要がある
Path.glob(pattern)patternに一致するパスを検索する。
patternには正規表現も利用可
Path.rglob(pattern)patternに一致するパスを再起的に検索する。
globに"**/"を付けて場合と同じ。

パスの一覧を取得: Path.iterdir()

Path.iterdir()は、指定されたパス直下のファイル/ディレクトリの一覧をイタレータとして取得します。
注意点として、iterdirではサブディレクトリまでは取得されない点です。(data配下は取得されていません。)
サブディレクトリまで取得したい場合は、後述のPath.glob()を用います。

top_dir = Path('./top_dir') # top_dirのパスを取得
print(type(top_dir.iterdir())) # <class 'generator'>

for文を使ってイタレータを出力してみます。

# iterdirの参照
for path in top_dir.iterdir():
    print(path)

### 実行結果
# top_dir/config.yaml
# top_dir/readme.md
# top_dir/data
# top_dir/src

top_dir直下のファイル/ディレクトリが参照されているのが分かります。

iterdirは、Pathがファイルを指している場合はNotADirectoryErrorとなります。

config_file = Path('./top_dir/config.yaml')
for path in config_file.iterdir():
    print(path)

### 実行結果
# NotADirectoryError: [Errno 20] Not a directory: 'top_dir/config.yaml'

パスの検索: Path.glob()

Path.glob()は、指定したパターンに一致するパスを検索します。検索には任意の文字列を意味するアスタリスク(*)などのワイルドカードが利用できます。また、iterdirと異なりサブディレクトリ配下を含めた再起的な検索も可能です。

top_dir = Path('./top_dir') # top_dirのパスを取得

# top_dir直下の全てのファイル/ディレクトリを検索
for path in top_dir.glob('*'):
    print(path)

### 実行結果
# top_dir/config.yaml
# top_dir/readme.md
# top_dir/data
# top_dir/src

サブディレクトリも含めて検索したい場合は、検索パターンに"**/"を追加します。
もしくはglobの代わりにrglobを用いても同じ結果が得られます。

# top_dir以下の.pngファイルを再起的に検索
for path in top_dir.glob('**/*.png'): # top_dir.rglob('*.png')でも同じ
    print(path)

### 実行結果
# top_dir/data/002.png
# top_dir/data/001.png

検索結果をリストとして取得したい場合は、内包表記が使えます。
また、検索結果をソートして取得したい場合は組み込み関数のsortedが使えます。

list_png = [path for path in top_dir.glob('**/*.png')] # .pngファイルのリスト
print(list_png)

### 実行結果
# [PosixPath('top_dir/data/002.png'), PosixPath('top_dir/data/001.png')]

# 昇順にソートする場合
sorted(list_png) # 降順にする場合は、reverse=Trueを指定。

### 実行結果
[PosixPath('top_dir/data/001.png'), PosixPath('top_dir/data/002.png')]

ファイルやディレクトリのみを検索したい場合は、Path.is_file(), Path.is_dir()が使えます。

list_file = [path for path in top_dir.glob('**/*') if path.is_file()] # ファイルのみ
print(list_file)

### 実行結果
# [PosixPath('top_dir/config.yaml'), PosixPath('top_dir/readme.md'), 〜〜略〜〜 PosixPath('top_dir/src/lib/utils.py')]

list_dir = [path for path in top_dir.glob('**/*') if path.is_dir()] # ディレクトリのみ
print(list_file)

### 実行結果
# [PosixPath('top_dir/data'), PosixPath('top_dir/src'), PosixPath('top_dir/src/lib')]

ファイルを開く:Path.open()

ファイルを開くときはopenメソッドが使えます。使い方は、組み込み関数のopenと同じように読み込み/書き込みのモードを指定して使います。

# ファイル操作
sample_file = Path('./top_dir/data/sample.txt')

# ファイルへ書き込み
with sample_file.open('w') as f:
    f.write('hello pathlib world\n')

# ファイルを読み込み
with sample_file.open('r') as f:
    buf = f.read()

print(buf) # 実行結果: hello pathlib world

存在しないファイルを開こうとするとFileNotFoundErrorとなるため、ファイルが存在する場合だけopenしたいときはexistsメソッドが使えます。existsメソッドはPathが既存のファイルかディレクトリを指している場合にTrueを返します。
existsメソッドを使って、ファイルが存在する場合だけopenを実行する方法は次のとおりです。

sample_file = Path('./top_dir/data/not_exists_file.txt')

if sample_file.exists(): # ファイルが存在する場合
    with sample_file.open('r') as f:
        buf = f.read()

    print(buf)

参考リンク

コメント

タイトルとURLをコピーしました