📂

pathlibが便利すぎてもうos.pathは使えない件

2023/11/06に公開

はじめに

こんにちは、@nano_sudoです!
皆さんは、普段何も考えずにos.pathを使っていませんか?
タイトルにもある通り、pathlibが便利すぎるので、今日はpathlibをマスターして、os.path地獄から脱出しましょう!

pathlibとは

pathlibは、ファイルパスを扱うためのライブラリです。
pathlibを使うと、ファイルパスを文字列で扱うのではなく、オブジェクトとして扱うことができます。

os.pathじゃだめなの?

pathlibは、os.pathと比べて、次のようなメリットがあります。

  • 豊富なメソッド
    pathlibは、ファイルパスをオブジェクトとして扱うので、ファイルパスに対して様々な操作を行うことができます。

  • パスの区切り文字の違いを吸収してくれる
    pathlibは、OSによって異なるパスの区切り文字を吸収してくれます。
    例えば、Windowsでは\、Linuxでは/を使いますが、pathlibを使えば、どちらのOSでも同じコードで動作させることができます。

  • パスの結合が簡単
    pathlibは、/演算子を使ってパスを結合することができます。
    例えば、/演算子を使って/home/user/home/user/test.txtを結合すると、/home/user/test.txtとなります。

主要なクラス

クラス構造図
pythonドキュメントより引用

基本的には、Pathクラスを使うことになりますが、 このクラスが出てきたときに混乱しないように、まずはクラスの構造を理解しておきましょう。

  • PurePath

    • システム非依存の一般的なパス操作を提供する基底クラス
    • パス文字列の解析、結合、分割などの基本操作を行う
  • PureWindowsPath

    • PurePathを継承した抽象クラス
    • Windowsシステム向けの純粋なパス操作を提供
  • PurePostixPath

    • PurePathを継承した抽象クラス
    • UNIX系システム向けの純粋なパス操作を提供
  • Path

    • パス操作の中心的なクラス
    • PurePathを継承し、ファイルシステム操作を追加
    • 実行環境のOSに応じて、自動的にWindowsPathまたはPosixPathのインスタンスを生成する
  • WindowsPath

    • Pathを継承したWindows向けのコンクリートクラス
    • Windowsシステムのパス表記やファイル操作に特化
    • 直接インスタンス化することはできない
  • PostixPath

    • Pathを継承したUNIX系向けのコンクリートクラス
    • UNIX系システムのパス表記やファイル操作に特化
    • 直接インスタンス化することはできない

早速使ってみる

パスの生成

pathlibは、Pathクラスを使ってパスを生成します。

from pathlib import Path

# 絶対パス
path = Path("/home/user/test.txt")

# 並べて書くと、結合される
path = Path("/home", "user", "test.txt") # /home/user/test.txt

# ただし、絶対パスを2つ以上並べて書くと、先に書いたパスは無視されるので注意
path = Path("/home", "/user", "test.txt") # /user/test.txt

# 相対パス
path = Path("test.txt")
# 現在のディレクトリ
path = Path.cwd()

パスの結合

/演算子を使ってパスを結合することができます。
(便利すぎて最初見たときは感動しました...)

from pathlib import Path

# Pathオブジェクト + Pathオブジェクト
path = Path("/home/user") / Path("test.txt")
# Pathオブジェクト + 文字列
path = Path("/home/user") / "test.txt"
# joinpathメソッド
path = Path("/home/user").joinpath("test.txt")

# 結合結果
print(path)  # /home/user/test.txt

パスの分解

from pathlib import Path

path = Path("/home/user/test.txt")

# ディレクトリ名
print(path.parent)  # /home/user
# ファイル名と拡張子
print(path.name)  # test.txt
# ファイル名(拡張子なし)
print(path.stem)  # test
# 拡張子
print(path.suffix)  # .txt

tar_gz = Path("/home/user/test.tar.gz")

# tar.gzなどの複数の拡張子を取得
print(tar_gz.suffixes)  # ['.tar', '.gz']
print("".join(tar_gz.suffixes))  # .tar.gz

パスの存在確認

os.path.existsと同じように、path.existsでパスの存在確認ができます。

from pathlib import Path

path = Path("/home/user/test.txt")

# パスが存在するか
print(path.exists())  # True
# パスがファイルか
print(path.is_file())  # True
# パスがディレクトリか
print(path.is_dir())  # False

相対パスと絶対パスの変換

Path.relative_to(<相対の基準となるパス>)で、絶対パスを相対パスに変換することができます。
Path.absolute()で、相対パスを絶対パスに変換することができます。
Path.resolve()で、...を含むパスを解決することができます。

from pathlib import Path

# 絶対パス
abspath = Path("/home/user/test.txt")
# 相対パス化
print(abspath.relative_to("/home"))  # user/test.txt

# 相対パス
relpath = Path("user/test.txt")
# 絶対パス化
print(relpath.absolute())  # /home/user/test.txt

# ..や.を含むパスを解決する
print(Path("user/../test.txt").resolve())  # /home/test.txt

パスの操作

pathlibでは、パスに対して様々な操作を行うことができます。

from pathlib import Path

path = Path("/home/user/test.txt")

# パスの移動・名前変更(上書きを行わない)
path.rename("/home/user/test2.txt")
# パスの移動・名前変更(上書きを行う)
path.replace("/home/user/test2.txt")
# パスの作成
path.mkdir() # parents=Trueで親ディレクトリも作成可能
# パスの削除
path.rmdir()

ファイルの操作

pathlibでは、ファイルに対して様々な操作を行うことができます。

from pathlib import Path

path = Path("/home/user/test.txt")

# open()と同じようにファイルを開く
with path.open(encoding="utf-8") as f:
    print(f.read())

# ファイルの読み込み
path.read_text()
# ファイルの書き込み
path.write_text("Hello World!")
# ファイルの削除
path.unlink() # ディレクトリの場合はrmdir()

jsonを読み込む例

from pathlib import Path
import json

# ファイルの読み込み
path = Path("/home/user/test.json")
data = json.loads(path.read_text())
# ファイルの書き込み
path.write_text(json.dumps(data))

open()を使うよりも、pathlibを使った方が、簡潔に書くことができます。
特に、ファイルを閉じる処理を書く必要がないので、コードが簡潔になります。

情報の取得

from pathlib import Path

fpath = Path("/home/user/test.txt")

# ファイルのサイズ
print(fpath.stat().st_size)  # 11
# ファイルの更新日時
print(fpath.stat().st_mtime)  # 1620000000.0
# ファイルの作成日時
print(fpath.stat().st_ctime)  # 1620000000.0

dpath = Path("/home/user")

# ディレクトリの中身を取得
print(list(dpath.iterdir()))  # [PosixPath('/home/user/test.txt')]
# ディレクトリの中身を再帰的に取得
print(list(dpath.glob("**/*")))  # [PosixPath('/home/user/test.txt')]
# ファイルを再帰的に取得
print(list(dpath.glob("**/*.txt")))  # [PosixPath('/home/user/test.txt')]

まとめ

いかがでしたでしょうか?
pathlibは、複雑なファイル構造を扱うときに、いつも重宝しています。
ぜひ使ってみて、良きPythonライフを送ってください!
間違いや、不明点があれば、コメントまたは、@nano_sudoまでお願いします!

Discussion