🐥

【Python】pathlib is All You Need

2022/05/01に公開

はじめに

はじめに,本記事で筆者が使用している環境や必要なライブラリ (任意) についてまとめます.

環境

PC MacBook Pro (16-inch, 2019)
OS Monterey
CPU 2.3 GHz 8コアIntel Core i9
メモリ 16GB
Python 3.9

ディレクトリ構成

root
├── assets
│   └── images
│       └── thumbnail.png
│
├── data
│   ├── 0000.png
│   ├── 0001.jpg
│   ├── 0002.png
│   ├── 0003.jpg
│   ...
└── main.py

使用するライブラリ

本記事で用いるライブラリとバージョンをまとめますが,特に気にせず

terminal
pip install opencv-python

で問題ないかと思います.今回は任意なのでライブラリはインストールしなくても問題ありません.

pathlibの魅力

ここでは,pathlibの魅力について実際のコードをもとに説明していきたいと思います.

pathlibと頻繁に比較されるライブラリとしてos.pathがあります.そこで2つのライブラリのシェア率を比較してみましょう.

trends_path
上記のGoogleの検索回数を見てみると2019年頃から,pathlibos.pathに比べて多く検索されている (5/2現在では約5倍) ことがわかります.

それではいよいよコードと共に魅力を伝えていきたいと思います.

ディレクトリ・ファイル名や拡張子の取得

pathlibのコード

main.py
from pathlib import Path

root = Path("root")
image = root / "assets/images/thumbnail.png"

print(image.parent)
# /root/assets/images
print(image.name)
# thumbnail.png
print(image.stem)
# thumbnail
print(image.suffix)
# png

os.pathのコード

main.py
from os import path

images = path.join(root, "assets/images")
image  = path.join(images, "thumbnail.png")

print(path.dirname(image))
# /root/assets/images
print(path.basename(image))
# thumbnail.png
print(path.splitext(path.basename(image))[0])
# thumbnail
print(path.splitext(path.basename(image))[-1])
# png

パスの連結

pathlibを用いると,パスの連結を簡単感覚的に扱うことができます.
その上,Windows,MacOS,Linux等の環境に依存せずに記述することができます.

pathlibのコード

main.py
from pathlib import Path


root = Path("root")
data = root / "data"
# PosixPath('/root/data')

os.pathを使ったコード

main.py
import os


root = "root"
data = os.path.join(root, "data")
# '/root/data'

ファイルの一括取得

pathlibを用いると,ファイルの一括取得を別のライブラリを用いる必要がなく実現できます.
そろそろこのあたりでpathlibの魅力が理解できると思います.

pathlibのコード

main.py
from pathlib import Path


root = Path("root")
data = root / "data"

images_path = [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
""" [PosixPath('/root/data/0000.png'),
PosixPath('/root/data/0001.jpg'),
PosixPath('/root/data/0002.png'),
PosixPath('/root/data/0003.jpg'),
...]
"""

os.pathを使ったコード

main.py
from os import path
from glob import glob


root = "root"
data = os.path.join(root, "data")
images_path = [p for p in glob(os.join(data, "*")) if (p.isfile(p) and path.splitext(p)[-1] in ["png", "jpg"])]

pathilbのデメリット

OpenCVjsonなど,pathlibを一部扱っていないライブラリが存在するため,pathlibを用いるとエラーが起きてしまうことがあります.

cv2.imreadでのエラー

main.py
import cv2
from pathlib import Path


images_path = [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
for path in images_path:
    image = cv2.imread(path)  # Error

これを実行すると,TypeError: Can't convert object to 'str' for 'filename' というエラーが出ます.これを防ぐにはエラー文にも書かれているように,strにキャストする必要があります.以下のようにキャストをしましょう

main.py
import cv2
from pathlib import Path


images_path = [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
for path in images_path:
-   # image = cv2.imread(path)
+   image = cv2.imread(str(path))
+   image = cv2.imread(path.as_posix())

json.dumpでのエラー

main.py
import json
from pathlib import Path


root = Path("root")
data = root / "data"
data_dict = {
    "images_path": [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
}

with open("path.json", "w") as f:
    json.dump(data, f)  # Error

これに対処するには3通りの方法があり,

  1. defaultでキャスト関数を用いる
  2. clsでキャストに使用するクラスを用いる

上記を順番に試してみましょう.

defaultでキャスト関数を用いる

main.py
import json
from pathlib import Path


root = Path("root")
data = root / "data"
data_dict = {
    "images_path": [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
}

with open("path.json", "w") as f:
-   # json.dump(data, f)  # Error
+   json.dump(data, f, default=str)

clsでキャストに使用するクラスを用いる

main.py
import json
from pathlib import Path


class JSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Path):
            return obj.as_posix()

root = Path("root")
data = root / "data"
data_dict = {
    "images_path": [p for p in data.glob("*") if (p.is_file and p.suffix in ["png", "jpg"])]
}

with open("path.json", "w") as f:
-   # json.dump(data, f)  # Error
+   json.dump(data, f, cls=JSONEncoder)

おわりに

本記事ではpathlibというライブラリの魅力について,実際のコードを用いながら説明してきました.
本記事で紹介したpathlibのメソッド等以外にも便利なメソッド等がたくさんあります.是非自分の調べてみましょう.

  • OSに依存しないパスの記述ができる
  • パスの連結が直感で (/) できる
  • オブジェクト志向なのでglobなどが扱いやすい

まとめると上記の3つが (今思い出せる限りでは) 魅力ということです.
今回紹介しているもの以外にも魅力があれば,是非共有して頂けると嬉しいです!

GitHubで編集を提案

Discussion