- Python
- 技術 💻
Python pathlibの使い方|ファイル操作逆引き70例と実行結果
pathlibとは、ファイルやフォルダのパスを文字列ではなく「Pathオブジェクト」として扱う、Python標準ライブラリのモジュールです。 Path("data") / "orders.csv" のように / でパスをつなぎ、p.suffix で拡張子、p.read_text() で中身を読むなど、パス操作とファイル操作を1つのオブジェクトで書けます。
この記事は 「やりたいこと→コード→実行結果」の逆引き形式で70例をまとめました。すべて実際のフォルダを作って Python 3.13.7(コピー・移動の新メソッドは 3.14.7)で実行した出力を載せています。さらに、同じコードを 3.11/3.12/3.13/3.14 で動かして比べた「バージョンごとの違い」、os.path との対応表、つまずきやすいポイント(+ で結合できない、文字コード)も紹介します。コードはすべてテキストなので、そのままコピーして使えます。
pathlib逆引き早見表
まずはこの表から探してください。 番号は本文のコード内コメントに対応しています。p = Path("/tmp/pathlib-demo/data/orders.csv") を例にしています。
| やりたいこと | コード | 結果の例 |
|---|---|---|
| パスをつなぐ | base / "data" / "orders.csv" |
/tmp/pathlib-demo/data/orders.csv |
| ファイル名 | p.name |
orders.csv |
| 拡張子なしの名前 | p.stem |
orders |
| 拡張子 | p.suffix |
.csv |
| 親フォルダ | p.parent |
/tmp/pathlib-demo/data |
| 拡張子を変える | p.with_suffix(".json") |
.../orders.json |
| 名前だけ変える | p.with_stem("orders_2026") |
.../orders_2026.csv |
| 存在確認 | p.exists() |
True |
| ファイルか・フォルダか | p.is_file() / p.is_dir() |
True / False |
| フォルダ作成(途中も) | d.mkdir(parents=True, exist_ok=True) |
― |
| テキストを読む | p.read_text(encoding="utf-8") |
文字列 |
| テキストを書く | p.write_text(s, encoding="utf-8") |
書いた文字数 |
| 直下の一覧 | d.iterdir() |
Pathのイテレータ |
| パターンで探す | d.glob("*.csv") |
〃 |
| サブフォルダも探す | d.rglob("*.csv") |
〃 |
| フォルダを巡回 | d.walk()(3.12+) |
(dirpath, dirnames, filenames) |
| コピー | p.copy(dst)(3.14+)/ shutil.copy2(p, dst) |
コピー先のPath |
| 名前変更 | p.rename(new) |
新しいPath |
| 移動(上書きあり) | p.replace(new) / p.move(new)(3.14+) |
新しいPath |
| ファイル削除 | p.unlink(missing_ok=True) |
― |
| 中身ごと削除 | shutil.rmtree(d) |
― |
| サイズ | p.stat().st_size |
37 |
| 更新日時 | datetime.fromtimestamp(p.stat().st_mtime) |
datetime |
| 相対パス | p.relative_to(base) |
data/orders.csv |
.. 付きの相対パス |
p.relative_to(other, walk_up=True)(3.12+) |
../data/orders.csv |
| 絶対パス | Path("x").resolve() |
正規化された絶対パス |
| カレント・ホーム | Path.cwd() / Path.home() |
/root など |
| スクリプトの場所 | Path(__file__).resolve().parent |
― |
| Windowsパスを解析 | PureWindowsPath(r"C:\a\b.txt") |
― |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python pathlibの使い方|ファイル操作逆引き70例と実行結果」
準備:サンプルフォルダを作る
以降のコードは、次のスクリプトで作ったフォルダで実行しています。 自分の環境でも同じ結果を確かめられます。
# setup_demo.py(サンプル用のフォルダとファイルを作る。何度実行してもよい)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
files = {
"data/orders.csv": "id,item,qty\n1,ノート,3\n2,ペン,5\n",
"data/members.csv": "id,name\n1,佐藤\n",
"data/report.txt": "在庫レポート\n",
"data/archive/2026-08.csv": "id,item,qty\n",
"docs/readme.md": "# README\n",
"docs/memo.TXT": "メモ\n",
"src/app.py": "print('hello')\n",
"src/utils/helper.py": "",
".env": "DEBUG=1\n",
}
for rel, text in files.items():
p = base / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(text, encoding="utf-8")
print("created:", base)
このほか、65 のスクリプトを scripts/tool.py として置いた状態で実行しています(36・41 の一覧に含まれます)。
実行環境は Linux(Python 3.13.7)です。Windowsでは区切り文字が \ で表示されます。
パスを作る・つなぐ(1〜5)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
p = base / "data" / "orders.csv" # 1. / で結合
print(p)
print(base.joinpath("data", "orders.csv")) # 2. joinpath で結合
print(Path("data", "orders.csv")) # 3. 複数の要素から作る
print(base / "/etc/hosts") # 4. 絶対パスを結合すると置き換わる
print(repr(p))
print(str(p), type(str(p))) # 5. 文字列に変換
/tmp/pathlib-demo/data/orders.csv
/tmp/pathlib-demo/data/orders.csv
data/orders.csv
/etc/hosts
PosixPath('/tmp/pathlib-demo/data/orders.csv')
/tmp/pathlib-demo/data/orders.csv <class 'str'>
/ の右側が / で始まる絶対パスだと、左側は捨てられて右側だけになります。 ユーザー入力などをつなぐときは、先頭の / に気をつけます。OSに合った区切り文字は pathlib が自動で使うので、"data/" + "orders.csv" のように自分で区切りを書く必要はありません。
名前・拡張子・親フォルダを取り出す(6〜14)
from pathlib import Path
p = Path("/tmp/pathlib-demo/data/archive/2026-08.csv")
print("name :", p.name) # 6. ファイル名
print("stem :", p.stem) # 7. 拡張子なしの名前
print("suffix :", p.suffix) # 8. 拡張子
print("suffixes:", Path("backup.tar.gz").suffixes) # 9. 複数の拡張子
print("parent :", p.parent) # 10. 親フォルダ
print("parents1:", p.parents[1]) # 11. 2つ上のフォルダ
print("parts :", p.parts) # 12. 要素に分解
print("anchor :", p.anchor) # 13. ルート部分
print("parent.name:", p.parent.name) # 14. 親フォルダの名前だけ
name : 2026-08.csv
stem : 2026-08
suffix : .csv
suffixes: ['.tar', '.gz']
parent : /tmp/pathlib-demo/data/archive
parents1: /tmp/pathlib-demo/data
parts : ('/', 'tmp', 'pathlib-demo', 'data', 'archive', '2026-08.csv')
anchor : /
parent.name: archive
suffix はピリオド付き(.csv)で返ります。 比較するときは p.suffix == ".csv"、大文字小文字を無視するなら p.suffix.lower() == ".csv" と書きます。
拡張子や名前を変えたパスを作る(15〜19)
from pathlib import Path
p = Path("data/orders.csv")
print(p.with_suffix(".json")) # 15. 拡張子を変える
print(p.with_suffix("")) # 16. 拡張子を外す
print(p.with_stem("orders_2026")) # 17. 拡張子はそのまま名前だけ変える(3.9+)
print(p.with_name("members.csv")) # 18. ファイル名ごと変える
print(p.with_stem(p.stem + "_bak")) # 19. 名前の後ろに付け足す
print(Path("backup.tar.gz").with_suffix(".zip")) # 最後の拡張子だけ変わる
data/orders.json
data/orders
data/orders_2026.csv
data/members.csv
data/orders_bak.csv
backup.tar.zip
with_〜 は新しいPathを返すだけで、実際のファイル名は変わりません。 ファイルの名前を変えるには、後述の rename() と組み合わせます(例:p.rename(p.with_suffix(".bak")))。
存在確認とファイル・フォルダの判定(20〜23)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
print((base / "data/orders.csv").exists()) # 20. 存在確認
print((base / "data/nothing.csv").exists())
print((base / "data").is_dir()) # 21. フォルダか
print((base / "data/orders.csv").is_file()) # 22. ファイルか
print((base / "data").is_file())
print(Path("orders.csv").is_absolute(), base.is_absolute()) # 23. 絶対パスか
True
False
True
True
False
False True
存在しないパスの is_file()・is_dir() は False を返すので、「あって、かつファイルか」は is_file() だけで確かめられます。
フォルダ・空ファイルを作る(24〜25)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
out = base / "output" / "2026" / "09"
out.mkdir(parents=True, exist_ok=True) # 24. 途中のフォルダもまとめて作る
print(out.is_dir())
out.mkdir(parents=True, exist_ok=True) # 2回目もエラーにならない
try:
(base / "output").mkdir() # exist_ok なし
except FileExistsError as e:
print("FileExistsError:", e)
try:
(base / "x" / "y").mkdir() # parents なし
except FileNotFoundError as e:
print("FileNotFoundError:", e)
log = base / "output" / "app.log"
log.touch() # 25. 空ファイルを作る
print(log.exists(), log.stat().st_size)
True
FileExistsError: [Errno 17] File exists: '/tmp/pathlib-demo/output'
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/pathlib-demo/x/y'
True 0
| 引数 | 省略時(False) | True のとき |
|---|---|---|
parents |
親がないと FileNotFoundError |
途中のフォルダもまとめて作る(mkdir -p 相当) |
exist_ok |
すでにあると FileExistsError |
すでにあってもエラーにしない |
「なければ作る」は mkdir(parents=True, exist_ok=True) の1行が定番です。ファイルを書く直前に p.parent.mkdir(parents=True, exist_ok=True) と書くと、保存先フォルダの準備も1行で済みます。
テキスト・バイナリを読み書きする(26〜32)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
p = base / "output" / "note.txt"
n = p.write_text("備品リスト\nノート\nペン\n", encoding="utf-8") # 26. 書き込み
print("written chars:", n)
text = p.read_text(encoding="utf-8") # 27. 読み込み
print(repr(text))
print(text.splitlines()) # 28. 行のリストにする
with p.open("a", encoding="utf-8") as f: # 29. 追記
f.write("消しゴム\n")
with p.open(encoding="utf-8") as f: # 30. 1行ずつ読む
for i, line in enumerate(f, 1):
print(i, line.rstrip("\n"))
b = base / "output" / "data.bin"
b.write_bytes(bytes([0, 1, 2, 255])) # 31. バイナリ書き込み
print(b.read_bytes()) # 32. バイナリ読み込み
written chars: 13
'備品リスト\nノート\nペン\n'
['備品リスト', 'ノート', 'ペン']
1 備品リスト
2 ノート
3 ペン
4 消しゴム
b'\x00\x01\x02\xff'
write_text()はファイルを上書きし、戻り値は書いた文字数です(バイト数ではありません)。- 追記は
open("a")を使います。write_text()には追記モードがありません。 - 大きなファイルは
open()で1行ずつ処理するとメモリを節約できます。 encodingは必ず指定します。省略するとOSの既定の文字コードになり、Windowsでは UTF-8 以外になることがあります(後述)。
ファイル一覧を取得・検索する(33〜40)
from pathlib import Path
base = Path("/tmp/pathlib-demo")
print(sorted(p.name for p in (base / "data").iterdir())) # 33. 直下の一覧
print(sorted(p.name for p in (base / "data").iterdir() if p.is_file())) # 34. ファイルだけ
print(sorted(p.name for p in (base / "data").glob("*.csv"))) # 35. 拡張子で絞る
print(sorted(str(p.relative_to(base)) for p in base.rglob("*.csv"))) # 36. サブフォルダも含めて
print(sorted(str(p.relative_to(base)) for p in base.glob("**/*.py"))) # 37. ** で再帰
print(sorted(p.name for p in base.glob("*"))) # 38. 隠しファイルも含む
print(sorted(p.name for p in (base / "docs").glob("*.txt")))
print(sorted(p.name for p in (base / "docs").glob("*.txt", case_sensitive=False))) # 39. 大文字小文字を無視(3.12+)
print(sorted(p.name for p in (base / "data").glob("*.[ct][sx][vt]"))) # 40. [] で文字の候補
['archive', 'members.csv', 'orders.csv', 'report.txt']
['members.csv', 'orders.csv', 'report.txt']
['members.csv', 'orders.csv']
['data/archive/2026-08.csv', 'data/members.csv', 'data/orders.csv']
['scripts/tool.py', 'src/app.py', 'src/utils/helper.py']
['.env', 'data', 'docs', 'output', 'scripts', 'src']
[]
['memo.TXT']
['members.csv', 'orders.csv', 'report.txt']
iterdir()・glob() の並び順は保証されないので、順番が必要なら sorted() で並べます。 glob() はジェネレータを返すため、件数を知りたいときや2回使いたいときは list() にします。
| パターン | 意味 | 例 |
|---|---|---|
* |
任意の文字列(/ を含まない) |
*.csv |
? |
任意の1文字 | 2026-0?.csv |
[abc] |
いずれか1文字 | *.[ct][sx][vt] |
** |
0個以上のフォルダ | **/*.py |
rglob("*.csv") |
glob("**/*.csv") と同じ |
― |
- Linux・macOSの
globは大文字小文字を区別します(*.txtでmemo.TXTは見つからない)。3.12以降はcase_sensitive=Falseで無視できます。 *は.envのようなドットで始まるファイルにも一致しました(シェルのls *とは違う点)。
フォルダを巡回する walk(41)
Path.walk()(3.12以降)は os.walk() のpathlib版で、フォルダごとに「パス・サブフォルダ名・ファイル名」を返します。
from pathlib import Path
base = Path("/tmp/pathlib-demo")
for dirpath, dirnames, filenames in base.walk(): # 41. walk(3.12+)
dirnames.sort()
dirnames[:] = [d for d in dirnames if d != "output"] # 除外したいフォルダ
print(dirpath.relative_to(base), sorted(filenames))
. ['.env']
data ['members.csv', 'orders.csv', 'report.txt']
data/archive ['2026-08.csv']
docs ['memo.TXT', 'readme.md']
scripts ['tool.py']
src ['app.py']
src/utils ['helper.py']
dirnames をその場で書き換えると、そのフォルダには入らなくなります(.git などを飛ばすときに使います)。
コピー・移動・名前変更・削除(42〜48)
3.13以前のコピーは shutil を使います。 pathlib の Path はそのまま shutil に渡せます。
import shutil
from pathlib import Path
base = Path("/tmp/pathlib-demo")
src = base / "data" / "orders.csv"
dst = base / "output" / "orders_copy.csv"
shutil.copy2(src, dst) # 42. ファイルのコピー(3.13以前)
print(dst.exists())
shutil.copytree(base / "docs", base / "output" / "docs_copy", dirs_exist_ok=True) # 43. フォルダごとコピー
print(sorted(p.name for p in (base / "output" / "docs_copy").iterdir()))
moved = dst.rename(base / "output" / "orders_renamed.csv") # 44. 名前変更
print(moved.name, dst.exists())
new = moved.replace(base / "output" / "2026" / "orders.csv") # 45. 移動(上書きあり)
print(new.relative_to(base))
tmp = base / "output" / "tmp.txt"
tmp.write_text("x", encoding="utf-8")
tmp.unlink() # 46. ファイル削除
tmp.unlink(missing_ok=True) # ないときもエラーにしない
print(tmp.exists())
empty = base / "output" / "empty"
empty.mkdir(exist_ok=True)
empty.rmdir() # 47. 空フォルダの削除
shutil.rmtree(base / "output" / "docs_copy") # 48. 中身ごとフォルダ削除
print(empty.exists(), (base / "output" / "docs_copy").exists())
True
['memo.TXT', 'readme.md']
orders_renamed.csv False
output/2026/orders.csv
False
False False
| メソッド | 移動先にファイルがあるとき | 用途 |
|---|---|---|
rename() |
Windowsではエラー、Linux・macOSでは上書き | 名前変更 |
replace() |
どのOSでも上書き | 確実に置き換えたいとき |
shutil.move() |
別ドライブへの移動にも対応 | ドライブをまたぐ移動 |
rename()・replace() は新しいパスを返します。 元の変数(dst)は古いパスのままなので、以降は戻り値を使います。
Python 3.14 の copy / copy_into / move / move_into(49〜52)
Python 3.14 から、Path に copy()・copy_into()・move()・move_into() が加わり、shutil なしでコピー・移動できます。 Python 3.14.7 で実行しました。
import sys
from pathlib import Path
print(sys.version.split()[0])
base = Path("/tmp/pathlib-demo")
src = base / "data" / "orders.csv"
r = src.copy(base / "output" / "orders_314.csv") # 49. Path.copy(3.14+)
print(r)
r = src.copy_into(base / "output") # 50. フォルダの中へコピー(3.14+)
print(r)
r = (base / "docs").copy(base / "output" / "docs314") # フォルダもコピーできる
print(sorted(p.name for p in r.iterdir()))
r = (base / "output" / "orders_314.csv").move(base / "output" / "2026" / "orders_314.csv") # 51. 移動(3.14+)
print(r)
r = (base / "output" / "docs314").move_into(base / "output" / "2026") # 52. フォルダの中へ移動(3.14+)
print(r)
3.14.7
/tmp/pathlib-demo/output/orders_314.csv
/tmp/pathlib-demo/output/orders.csv
['memo.TXT', 'readme.md']
/tmp/pathlib-demo/output/2026/orders_314.csv
/tmp/pathlib-demo/output/2026/docs314
copy() はファイルにもフォルダにも使え、戻り値はコピー先のPathです。 〜_into() は「そのフォルダの中に同じ名前で」置きます。3.13で copy() を呼ぶと AttributeError: 'PosixPath' object has no attribute 'copy' になりました。
ファイルサイズ・更新日時(53〜56)
from datetime import datetime
from pathlib import Path
p = Path("/tmp/pathlib-demo/data/orders.csv")
st = p.stat()
print("size :", st.st_size, "bytes") # 53. ファイルサイズ
print("mtime:", datetime.fromtimestamp(st.st_mtime).strftime("%Y-%m-%d %H:%M")) # 54. 更新日時
latest = max(Path("/tmp/pathlib-demo/data").glob("*.csv"), key=lambda x: x.stat().st_mtime)
print("latest:", latest.name) # 55. 最新のファイル
total = sum(f.stat().st_size for f in Path("/tmp/pathlib-demo").rglob("*") if f.is_file())
print("total:", total, "bytes") # 56. フォルダの合計サイズ
size : 37 bytes
mtime: 2026-09-29 13:04
latest: members.csv
total: 560 bytes
orders.csv は日本語を含むため、文字数(27文字)ではなく UTF-8 のバイト数(37バイト)になります。st_mtime はエポック秒なので、datetime.fromtimestamp() で日時に変換します。
相対パス・絶対パス・ホーム・スクリプトの場所(57〜65)
import os
from pathlib import Path
base = Path("/tmp/pathlib-demo")
p = base / "data" / "archive" / "2026-08.csv"
print(p.relative_to(base)) # 57. 相対パスにする
try:
print(p.relative_to(base / "docs"))
except ValueError as e:
print("ValueError:", e)
print(p.relative_to(base / "docs", walk_up=True)) # 58. .. を使った相対パス(3.12+)
print(os.path.relpath(p, base / "docs")) # 3.11以前はこちら
print(p.is_relative_to(base), p.is_relative_to("/etc")) # 59. 配下かどうか
os.chdir(base / "src")
print(Path.cwd()) # 60. カレントフォルダ
print(Path("../data/orders.csv").absolute()) # 61. 絶対パス(.. は残る)
print(Path("../data/orders.csv").resolve()) # 62. 正規化した絶対パス
print(Path.home()) # 63. ホームフォルダ
print(Path("~/work").expanduser()) # 64. ~ を展開
data/archive/2026-08.csv
ValueError: '/tmp/pathlib-demo/data/archive/2026-08.csv' is not in the subpath of '/tmp/pathlib-demo/docs'
../data/archive/2026-08.csv
../data/archive/2026-08.csv
True False
/tmp/pathlib-demo/src
/tmp/pathlib-demo/src/../data/orders.csv
/tmp/pathlib-demo/data/orders.csv
/root
/root/work
| メソッド | .. の扱い |
シンボリックリンク | 使いどころ |
|---|---|---|---|
absolute() |
そのまま残す | 解決しない | 見た目だけ絶対パスにしたい |
resolve() |
取り除く | 解決する | 比較・保存用の正規化したパス |
スクリプトと同じフォルダのファイルを読むときは、カレントフォルダではなく __file__ を基準にします。 /tmp/pathlib-demo/scripts/tool.py として保存し、別のフォルダ(/tmp)から実行しました。
# scripts/tool.py
from pathlib import Path
HERE = Path(__file__).resolve().parent # 65. このスクリプトのフォルダ
print(HERE.name)
config = HERE / "config.toml" # スクリプトの隣のファイル
print(config.name, config.parent == HERE)
scripts
config.toml True
Path("config.toml") はカレントフォルダ基準なので、実行場所で結果が変わります。__file__ 基準ならどこから実行しても同じ場所を指せます。
一時フォルダを使う(66)
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as d: # 66. 一時フォルダ
tmp = Path(d)
(tmp / "work.txt").write_text("一時データ", encoding="utf-8")
print(tmp.parent, [p.name for p in tmp.iterdir()])
saved = tmp
print(saved.exists()) # with を抜けると消える
/tmp ['work.txt']
False
TemporaryDirectory() は文字列を返すので、Path(d) で包んで使います。 pytest なら tmp_path フィクスチャが最初から Path で渡されます。
Windowsのパスを扱う(67〜69)
PureWindowsPath を使うと、Linux・macOS上でもWindows形式のパスを解析できます。 ファイルにはアクセスせず、文字列としての操作だけを行うクラスです。
from pathlib import PurePosixPath, PureWindowsPath, Path
w = PureWindowsPath(r"C:\Users\taro\Documents\report.xlsx") # 67. Windowsパスを扱う
print(w.name, w.suffix, w.drive, w.parent)
print(w.as_posix()) # 68. / 区切りにする
print(PureWindowsPath("C:/data") / "orders.csv") # / で書いても \ になる
print(PureWindowsPath("DATA/Orders.CSV") == PureWindowsPath("data/orders.csv")) # Windowsは大文字小文字を区別しない
print(PurePosixPath("DATA/Orders.CSV") == PurePosixPath("data/orders.csv"))
print(Path("/tmp/pathlib-demo/data/orders.csv").as_uri()) # 69. file:// URI
print(PureWindowsPath(r"\\server\share\file.txt").drive) # UNCパス
report.xlsx .xlsx C: C:\Users\taro\Documents
C:/Users/taro/Documents/report.xlsx
C:\data\orders.csv
True
False
file:///tmp/pathlib-demo/data/orders.csv
\\server\share
- Windowsのパスを文字列で書くときは
r"..."(raw文字列)にします。"C:\new"のように書くと\nが改行として解釈されます。 - Windows上でも
Path("C:/data/orders.csv")と/で書いて問題ありません。 pathlib が\に直して扱います。
パターンに一致するか判定する(70)
from pathlib import PurePath
q = PurePath("data/archive/2026-08.csv")
print(q.match("*.csv"), q.match("data/*.csv")) # match は右から比べる
print(q.full_match("data/**/*.csv"), q.full_match("*.csv")) # 70. 全体で比べる(3.13+)
True False
True False
match() はパスの右端から比べ、full_match()(3.13以降)はパス全体で比べます。 ** を使った判定には full_match() を使います。
Python 3.11〜3.14 でのpathlibの違い(実行比較)
同じコードを4つのバージョンで実行した結果です。 業務で古いバージョンを使っている場合の確認に使えます。
| 機能 | 3.11.15 | 3.12.3 | 3.13.7 | 3.14.7 |
|---|---|---|---|---|
walk() |
AttributeError |
使える | 使える | 使える |
relative_to(..., walk_up=True) |
TypeError |
使える | 使える | 使える |
glob(..., case_sensitive=False) |
TypeError |
使える | 使える | 使える |
with_segments() |
AttributeError |
使える | 使える | 使える |
glob("data/**") の結果 |
フォルダだけ(data、data/archive) |
フォルダだけ | ファイルも含む(6件) | ファイルも含む |
full_match() |
AttributeError |
AttributeError |
使える | 使える |
Path.from_uri() |
AttributeError |
AttributeError |
使える | 使える |
read_text(newline=...) |
TypeError |
TypeError |
使える | 使える |
copy()・move() など |
AttributeError |
AttributeError |
AttributeError |
使える |
Path("file.").suffix |
'' |
'' |
'' |
'.' |
with_suffix(".") |
ValueError: Invalid suffix '.' |
同左 | 同左 | a. |
特に注意したいのは、3.13で ** で終わるパターンの結果が変わった点です。 3.12までは glob("data/**") がフォルダだけを返していましたが、3.13以降はファイルも返します(公式ドキュメントの「Changed in version 3.13」にも記載)。フォルダだけが欲しい場合は、if p.is_dir() で絞り込むとどのバージョンでも同じ結果になります。
os.path との対応表
os.path から移行するときの対応表です。 左の書き方は今も使えますが、pathlib にすると1つのオブジェクトで完結します。
| やりたいこと | os / os.path | pathlib |
|---|---|---|
| 結合 | os.path.join(a, "b.csv") |
Path(a) / "b.csv" |
| ファイル名 | os.path.basename(p) |
p.name |
| 親フォルダ | os.path.dirname(p) |
p.parent |
| 拡張子で分ける | os.path.splitext(p) → ('backup.tar', '.gz') |
p.stem、p.suffix |
| 存在確認 | os.path.exists(p) |
p.exists() |
| 絶対パス | os.path.abspath(p) |
p.absolute()(.. を消すなら p.resolve()) |
| 正規化 | os.path.realpath(p) |
p.resolve() |
| 相対パス | os.path.relpath(p, start) |
p.relative_to(start, walk_up=True)(3.12+) |
| 一覧 | os.listdir(d) |
d.iterdir() |
| パターン検索 | glob.glob("*.csv") |
d.glob("*.csv") |
| 巡回 | os.walk(d) |
d.walk()(3.12+) |
| フォルダ作成 | os.makedirs(d, exist_ok=True) |
d.mkdir(parents=True, exist_ok=True) |
| ファイル削除 | os.remove(p) |
p.unlink() |
| 名前変更 | os.rename(a, b) |
a.rename(b) |
| サイズ | os.path.getsize(p) |
p.stat().st_size |
| 更新日時 | os.path.getmtime(p) |
p.stat().st_mtime |
| 読み込み | open(p, encoding=...).read() |
p.read_text(encoding=...) |
open()・shutil・pandas.read_csv() など多くの関数は Path をそのまま受け取れます(os.PathLike に対応しているため)。str(p) への変換が必要なのは、文字列を前提とした古いライブラリや、文字列として連結したい場合だけです。
よくあるミスと直し方
実際に実行して確認したエラーと結果です。
| ミス | 実際の結果 | 直し方 |
|---|---|---|
base + "/data" |
TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str' |
base / "data" |
"demo" in str(p) で判定 |
途中の文字列にも一致する | p.name ==、p.suffix == で比べる |
cp932(Shift_JIS)のファイルを encoding="utf-8" で読む |
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x94 in position 0: invalid start byte |
p.read_text(encoding="cp932") → 備品 |
BOM付きUTF-8(encoding="utf-8-sig" で保存)を utf-8 で読む |
先頭3バイト b'\xef\xbb\xbf' が残り、.startswith("id") が False |
encoding="utf-8-sig" で読むと True |
encoding を省略 |
OSの既定の文字コードで読み書きされる | 常に encoding="utf-8" などを明記 |
with_suffix() だけ呼ぶ |
新しいPathを返すだけでファイル名は変わらない | p.rename(p.with_suffix(".bak")) |
Path("data/") |
末尾の / は消えて data になる(Path("data/") == Path("data") は True) |
フォルダかどうかは is_dir() で判定 |
よくある質問
Q. pathlib と os.path はどちらを使うべきですか? A. 新しく書くコードでは pathlib が読みやすく、結合・名前の取得・読み書きまでを1つのオブジェクトで扱えます。既存コードの os.path はそのまま動くので、触る部分から上の対応表を使って少しずつ置き換えると安全です。
Q. Path と PurePath の違いは?
A. PurePath はパスを文字列として操作するだけで、ファイルにアクセスしません。Path は PurePath の機能に加えて、存在確認や読み書きなどのファイル操作ができます。普段は Path、別のOS形式のパスを解析するときは PureWindowsPath・PurePosixPath を使います。
Q. glob の結果が空になります。
A. 大文字小文字の違い(Linux・macOS)、カレントフォルダの違い、パターンの書き間違いの順に確認します。print(Path.cwd()) で基準の場所を確かめ、3.12以降なら case_sensitive=False も試せます。
まとめ
- pathlib は
/でパスをつなぎ、name・stem・suffix・parentで要素を取り出します。 - フォルダ作成は
mkdir(parents=True, exist_ok=True)、読み書きはread_text/write_textにencodingを明記します。 - 一覧は
iterdir()、検索はglob()・rglob()、巡回はwalk()(3.12+)。順番が必要ならsorted()を使います。 - 3.14 から
copy()・move()が加わり、3.13以前はshutilと組み合わせます。 relative_to(..., walk_up=True)は 3.12+、glob("**")がファイルも返すのは 3.13+ など、バージョンによる違いに気をつけます。- スクリプトの隣のファイルは
Path(__file__).resolve().parentを基準にするとどこから実行しても同じ場所を指せます。 +ではなく/、文字化けはencodingの指定で解決できます。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python pathlibの使い方|ファイル操作逆引き70例と実行結果」
株式会社RJCでは、pathlibのような標準ライブラリを使いこなして読みやすいコードを書く工夫を、プロジェクトの中で仲間と共有しています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。