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

# はじめに

こんにちは、[@nano_sudo](https://twitter.com/nano_sudo)です!
今回は、Pythonの標準ライブラリである`pathlib`を紹介します。(n番煎じ...)
タイトルは正直なところ、少し大げさですが、`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`となります。


# 主要なクラス

![pathlib-inheritance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3535890/c471061f-8d47-28f3-b7a8-4ed708e2a4d5.png)
[pythonドキュメント](https://docs.python.org/ja/3/library/pathlib.html)より引用

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

- `PurePath`
  OSに依存しないパスを表現するためのクラス
- `PurePosixPath`
  LinuxやMacOSのパスを表現するためのクラス
- `PureWindowsPath`
  Windowsのパスを表現するためのクラス
- `Path`
  `PurePath`を継承したクラスで、`PurePath`にファイルシステムの操作を追加したクラス
- `WindowsPath`,`PosixPath`
  `Path`を継承したクラスで、`Path`にOSに依存したファイルシステムの操作を追加したクラス
  親クラスは、OSに依存しない`PureWindowsPath`,`PurePosixPath`と`Path`

# 早速使ってみる



## パスの生成

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

```python
from pathlib import Path

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

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

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

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

## パスの結合

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

```python
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
```

## パスの分解
```python
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
```
注意点としては、`path.suffix`は`tar.gz`のような複数の拡張子には対応していないので、`path.name`を使って自分で処理する必要があります。

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

```python
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()`で、`..`や`.`を含むパスを解決することができます。

```python
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`では、パスに対して様々な操作を行うことができます。

```python
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`では、ファイルに対して様々な操作を行うことができます。

```python
from pathlib import Path

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

# ファイルの読み込み
path.read_text()
# ファイルの書き込み
path.write_text("Hello World!")
# ファイルの削除
path.unlink() # ディレクトリの場合はrmdir()
```
jsonを読み込む例
```python
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`を使った方が、簡潔に書くことができます。
特に、ファイルを閉じる処理を書く必要がないので、コードが簡潔になります。

## 情報の取得

```python
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](https://twitter.com/nano_sudo)までお願いします！