\ 最大10%ポイントアップ! /

【Python】pathlibライブラリの使い方

  • Pathライブラリの使い方が知りたい!

このような方に向けて書きました。

Python でパス操作するときは os ライブラリを使う方が多いかもしれません。

ところが Python 3.4 から標準ライブラリとして搭載されてからは、Pathlibライブラリが使われることも徐々に増えてきました。

例えば Python の Web フレームワーク Django でも、バージョン2.0からは os の代わりに pathlib が使われています。

pathlibosを完全に置き換えるものではありませんが、よりシンプルにパスを表現できるので圧倒的に便利です。

この Path ライブラリは、次の操作が可能です。

  • ファイルの作成
  • ファイルの削除
  • パスの結合
  • ファイルの移動など

本記事では代表的な操作を網羅したので、大体の実装の悩みにお答えできると思います。

記事が長くなってしまったので、以下の目次のリンクより該当箇所に飛んでください。

osライブラリとpathlibライブラリを比較してみる

パス操作ライブラリはosが有名ですが、pathlibを使う方が圧倒的に便利です。

pathlibの方が優れている点は次のとおりです。

  • シンプルに書ける
  • OSの違いを気にしないで書ける
  • メソッドが多く可読性が上がる

それぞれ解説します。

シンプルに書ける

pathlib ではパスをオブジェクトとして扱うことができるので、osよりもシンプルに書けます。

例えば、あるパスの一つ上の階層のパスを取得する場合。

# osの場合
parent_dir = os.path.abspath(os.path.join("some/directory", os.pardir))

# pathlibの場合
parent_dir = Path("some/directory").parent

カレントディレクトリ配下の全ファイルを取得する場合。

# osの場合
files = []
for root, dirs, files_in_dir in os.walk("."):
    for file in files_in_dir:
        files.append(os.path.join(root, file))

# pathlibの場合
files = list(Path(".").rglob("*"))

どちらもシンプルで可読性が上がります。

OSの違いを気にしないで書ける

OSごとにパスの扱いは異なります。

# Windowsのファイルパス例
"C:\\Users\\YourName\\Documents\\file.txt"

# Unix系(Linux, macOS)のファイルパス例
"/home/yourname/documents/file.txt"

pathlib ではパスをオブジェクト化してくれるので、これらの違いを考える必要がなくなります。

つまり、それぞれパスオブジェクトに格納してしまえば自由にpathlibのメソッドが適用可能です。

# Windowsのファイルパス例
windows_path = Path("C:\\Users\\YourName\\Documents\\file.txt")
print(windows_path.parent)  # C:\Users\YourName\Documents

# Unix系(Linux, macOS)のファイルパス例
unix_path = Path("/home/yourname/documents/file.txt")
print(unix_path.parent)     # /home/yourname/documents

メソッドが多く可読性が上がる

osライブラリに比べてメソッドが多いので、短いコードでさまざまなことができます。

from pathlib import Path

# カレントディレクトリから 'example_dir' ディレクトリを作成し、'example_file.txt' ファイルのパスを生成する例
# pathlib を使用
path = Path.cwd() / 'example_dir' / 'example_file.txt'
path.parent.mkdir(parents=True, exist_ok=True)  # example_dir ディレクトリを作成
path.touch()  # example_file.txt ファイルを作成

print(f"Path created: {path}")

上記のコードをosで書くと以下になります。

import os

# カレントディレクトリから 'example_dir' ディレクトリを作成し、'example_file.txt' ファイルのパスを生成する例
# os を使用
path = os.path.join(os.getcwd(), 'example_dir', 'example_file.txt')
os.makedirs(os.path.dirname(path), exist_ok=True)  # example_dir ディレクトリを作成
with open(path, 'w'):  # example_file.txt ファイルを作成
    pass

print(f"Path created: {path}")

比較すると、pathlibの方が可読性が高いことを感じていただけると思います。

基本操作

pathlibの基本的な操作から説明します。

全ての操作の基本になりますので、特にパスオブジェクトの生成はマスターしてください。

パスオブジェクトを生成

ファイルやディレクトリを扱うためのPathオブジェクトを生成します。

from pathlib import Path

# ファイルパスを表すPathオブジェクトを作成
path = Path("/path/to/file.txt")

パスを文字列ではなく、Pythonオブジェクトとして扱うための重要なステップです。

なお、PathオブジェクトはOSに関わらず取得可能です。

以下はWindowsの場合です。

from pathlib import Path

# Windowsパスの例
windows_path = Path("C:/Users/Username/Documents")
print(windows_path.exists())  # パスの存在確認

Linuxでも問題なくパスオブジェクトを取得できます。

from pathlib import Path

# Linuxパスの例
linux_path = Path("/home/user/projects")
print(linux_path.exists())  # パスが存在するか確認

このようにOSごとにパスの違いを意識することなく実装できるのは、とても便利です。

複数のパスを連結

パスの結合方法は、いくつかのバリエーションが用意されています。

from pathlib import Path

# joinpathメソッドを使う方法
path = Path("/path/to").joinpath("the", "file.txt")
print(path)  # /path/to/the/file.txt

# '/'演算子を使う方法
path = Path("/path/to") / "the" / "file.txt"
print(path)  # /path/to/the/file.txt

# 複数引数で結合
path = Path("/path/to", "the", "file.txt")
print(path)  # /path/to/the/file.txt

osライブラリではos.path.joinしかできなかったので、これだけの種類があるとかえって戸惑うかもしれません。

プログラミング全般に言えることですが、コード全体を通して一貫性を持たせることが大事です。

なので、可読性を上げるために結合にあたって使うコードを決めておくと良いです。

絶対パスを取得する

絶対パスはresolveメソッドだけで実装できます。

from pathlib import Path

path = Path("example.txt")
absolute_path = path.resolve()
print(absolute_path)

相対パスを取得

一方の相対パスは、relative_toメソッドを使います。

from pathlib import Path

# 相対パスを取得
path = Path("/path/to/file.txt")
relative_path = path.relative_to("/path")
print(relative_path)  # to/file.txt

relative_toの引数には、どのパスを基準に相対パスを取得するか(つまり、基準となるパス)を渡します。

パスの存在確認

存在確認にはexistsメソッドを使います。

from pathlib import Path

# ファイルやディレクトリの存在確認
path = Path("/path/to/file.txt")
if path.exists():
    print("存在します")

パスオブジェクトをPOSIXスタイルに変換

バックスラッシュが使われている Windows パスをスラッシュ区切りにする際に使います。

from pathlib import Path

windows_path = Path("C:\\Program Files\\Example")
print(windows_path.as_posix())  # "C:/Program Files/Example"

ディレクトリ操作

Pathオブジェクトを使ったディレクトリ操作方法をご紹介します。

ディレクトリを作成

ディレクトリの作成は、mkdirメソッドを使います。

from pathlib import Path

# ディレクトリを作成
path = Path("/path/to/new_directory")
path.mkdir()

親ディレクトリもまとめて作りたい場合にはparents=Trueを、ディレクトリがない場合だけ作りたい場合にはexist_ok=Trueを引数に渡します。

from pathlib import Path

# ディレクトリを作成
path = Path("/path/to/new_directory")
path.mkdir(parents=True, exist_ok=True)

ディレクトリの削除

ディレクトリの削除にはrmdirメソッドを使います。

from pathlib import Path

# 空のディレクトリを削除
path = Path("/path/to/directory")
path.rmdir()

ディレクトリか判定

対象のPathオブジェクトにis_dirメソッドを使うと、ディレクトリかどうかの判定ができます。

from pathlib import Path

# ディレクトリかどうかを判定
path = Path('new_directory')
print(path.is_dir())

ファイルかどうかを判定するにはis_fileを使います。
詳しくはこちら

ディレクトリが空かどうか確認

ディレクトリが空かどうかを判定する直接のメソッドはないので、次のようにnot anyを使って実装します。

from pathlib import Path

# ディレクトリが空かどうか確認
path = Path('new_directory')
is_empty = not any(path.iterdir())
print(is_empty)

iterdirは、対象のディレクトリにあるファイルやフォルダのイテレータを取得するメソッドです。

もしディレクトリが空の場合、iterdirの結果は空のイテレータになります。このため、any(path.iterdir())の結果はFalseになります。

今回は「空かどうか」を判定したいため、notをつけてis_emptyとしました。これにより、is_emptyTrueであればディレクトリは空であることが分かります。

ディレクトリ一覧を取得

ディレクトリ一覧を取得するには、iterdirメソッドとis_dirメソッドを併用します。

from pathlib import Path

# ディレクトリ一覧を取得
directories = [d for d in Path('.').iterdir() if d.is_dir()]
print(directories)

iterdirメソッドは配下のファイルとフォルダを取得するので、条件としてis_dirメソッドでディレクトリに絞り込んでいます。

仮に再帰性を持たせるとしたら、以下のようにrglobを使います。

from pathlib import Path

# 再帰的に全てのディレクトリを取得
directories = [d for d in Path('.').rglob('*') if d.is_dir()]
print(directories)

親ディレクトリの名前を変更

from pathlib import Path

path = Path("/home/old_folder/file.txt")
new_path = path.with_name("new_folder") / path.name
print(new_path)  # /home/new_folder/file.txt

ファイル操作

新しいファイルを作成

ファイルの作成には、touchメソッドが用意されています。

from pathlib import Path

# 新しいファイルを作成
path = Path("/path/to/new_file.txt")
path.touch()
karo

Linuxのtouchコマンドに似ていて覚えやすい

ファイルの削除

ファイルの削除はunlinkメソッドを使います。missing_ok=Trueとすると、ファイルがない場合でもエラーを出しません。

from pathlib import Path

# ファイルを削除
path = Path("/path/to/file.txt")
path.unlink(missing_ok=True)

ファイル名の変更

ファイル名の変更はrenameメソッドを使います。

from pathlib import Path

# ファイル名を変更
old_path = Path("/path/to/old_file.txt")
new_path = Path("/path/to/new_file.txt")
old_path.rename(new_path)

ファイルを移動

ファイル名ではなくディレクトリ名に対してremameメソッドを使うと、ファイルの移動ができます。

from pathlib import Path

# ファイルを移動
source = Path("/path/to/file.txt")
destination = Path("/new/path/to/file.txt")
source.rename(destination)

ファイルをコピーする

残念ながらpathlibにはコピーするメソッドは用意されていないので、shutil.copyでコピーを行いましょう。

import shutil
from pathlib import Path

src = Path("file.txt")
dst = Path("copy_of_file.txt")
shutil.copy(src, dst)

複数ファイルをコピーする場合は、次のようにループで回します。

import shutil
from pathlib import Path

files = [Path("file1.txt"), Path("file2.txt")]
for file in files:
    shutil.copy(file, Path("destination_directory") / file.name)

ファイルかどうかを判定

ファイルかどうかを判定するにはis_fileメソッドを使います。

from pathlib import Path

# パスを指定
path = Path("example.txt")

# ファイルかどうかを判定
if path.is_file():
    print(f"{path} はファイルです。")
else:
    print(f"{path} はファイルではありません。")

ディレクトリかどうかを判定したい時はis_dirメソッドを使いましょう。
詳しくはこちらです。

拡張子を変更

with_suffixメソッドを使えば拡張子の変更もラクラクです。

from pathlib import Path

path = Path("/home/user/documents/file.txt")
new_path = path.with_suffix(".md")
print(new_path)  # /home/user/documents/file.md

検索

ファイル名を検索

globメソッドを実行することで、ファイル名の検索ができます。

特定の拡張子を持つファイルを取得する場合は、ワイルドカードに続けて拡張子名を指定します。

from pathlib import Path

# 特定の拡張子(例:.txt)のファイルをリストアップ
txt_files = list(Path('.').glob('*.txt'))
print(txt_files)

複数の拡張子に対して検索をかけたい場合は、次のようにそれぞれの結果を取得して統合します。

from pathlib import Path
import itertools

# 複数の拡張子に一致するファイルを取得
txt_files = Path(".").glob("*.txt")
md_files = Path(".").glob("*.md")
all_files = itertools.chain(txt_files, md_files)

for file in all_files:
    print(file)

ディレクトリ内を再帰的に探索

再帰的にディレクトリを探索するときはglob('**/*') を使います。

from pathlib import Path

# 再帰的にディレクトリ内を探索
path = Path('.')
for file_path in path.glob('**/*'):
    print(file_path)

ファイル一覧を取得

ファイルの一覧を取得する場合は、iterdirメソッドとis_fileメソッドを組み合わせます。

from pathlib import Path

# 現在のディレクトリ内のファイル一覧を取得
files = [f for f in Path('.').iterdir() if f.is_file()]
print(files)

ディレクトリの一覧を取得する場合は、is_dirメソッドと組み合わせます。

ディレクトリを移動

ディレクトリの移動はpathlibにないので、osライブラリと併用します。

from pathlib import Path

# 現在のディレクトリを変更
import os
os.chdir(Path('new_directory'))

ファイル名やディレクトリ名を取得

ベースネーム(ファイル名.拡張子)部分だけを取得する方法です。

from pathlib import Path

# ディレクトリ名を取得
path = Path('new_directory')
print(path.name)
from pathlib import Path

path = Path("/home/user/documents/file.txt")
print(path.name)  # file.txt

ディレクトリ部分だけを取得

parentとすることで、ベースネームを除いたディレクトリ部分を取得できます。

from pathlib import Path

path = Path("/home/user/documents/file.txt")
print(path.parent)  # /home/user/documents

ちなみに二つ上の階層は.parent.parent、三つ上の階層は.parent.parent.parentで取れます。

拡張子だけを取得

拡張子だけをとるならsuffixメソッドを使います。

from pathlib import Path

path = Path("/home/user/documents/file.txt")
print(path.suffix)  # .txt

globを使った方法

glob メソッドを使うと、ワイルドカード(*?)でパターンに一致するファイルやディレクトリを再帰的に取得できます。

検索・操作するために必須の知識なので、ぜひマスターしていきましょう。

カレントディレクトリの全てのファイルを取得

全ファイルを取得する場合には、globメソッドの引数に*を指定します。

from pathlib import Path

# カレントディレクトリ内のすべてのファイルを取得
files = Path(".").glob("*")
for file in files:
    print(file)

このコードに再帰性はないので、カレントディレクトリ直下のファイルのみ収集することになります。

パターンに一致するファイルやディレクトリを取得

globメソッドの引数にパターンを指定することで、条件にマッチしたファイルを取得できます。

例えば、以下は.txtという拡張子を持つファイルだけを取得したい場合です。

from pathlib import Path

# カレントディレクトリ内のすべてのテキストファイルを取得
files = Path(".").glob("*.txt")
for file in files:
    print(file)

並び替えのメソッドはないので、sortedメソッドで行います。

from pathlib import Path

# ファイルをソートして表示
files = sorted(Path(".").glob("*.txt"))
for file in files:
    print(file)

正規表現を使う

Pathオブジェクトに正規表現検索する直接のメソッドはないので、reライブラリと併用して検索を行います。

import re
from pathlib import Path

# 正規表現でファイル名をフィルタ
pattern = re.compile(r"file_\d+.txt")
files = Path(".").glob("*.txt")
for file in files:
    if pattern.match(file.name):
        print(file)

大文字・小文字を無視する

lower関数とendswithメソッドを使うことで.txt.TXTなど大文字・小文字を区別せずに全て取得できます。

from pathlib import Path

# 大文字小文字を無視してフィルタリング
files = Path(".").glob("*")
for file in files:
    if file.name.lower().endswith(".txt"):
        print(file)

その他の特殊な操作

ここまで紹介してきた操作以外の、ちょっと特殊な操作方法をご紹介します。

「こんなこともできるのか!」といった感じでお楽しみいただければと思います。

Windowsのショートカットを扱う

pathlibにはWindowsのショートカットを直接扱う機能がないので、win32comを使って実現します。

import win32com.client

# ショートカットから実際のパスを取得する
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut("C:/path/to/shortcut.lnk")
print(shortcut.Targetpath)

ちなみにwin32comを使うとExcelやOutlookなどの操作もできるので、興味のある方は試してみてください。

詳しくは以下の記事で解説しました。

ディレクトリをコピーする

ディレクトリのコピーはpathlibに含まれないので、shutilライブラリで実装します。

import shutil
from pathlib import Path

src = Path("source_directory")
dst = Path("destination_directory")
shutil.copytree(src, dst)

パス操作を模擬する

Path.exists メソッドをモックして、テスト時に常に指定したパスが存在するように見せかける方法です。

unittest.mock.patchを使います。

from unittest.mock import patch
from pathlib import Path

# パスの存在確認をモック
with patch('pathlib.Path.exists', return_value=True):
    path = Path("/mock/path")
    print(path.exists())  # True

これにより、実際にはパスの存在確認をせずに常にTrueを返すことができます。

このコードはテストコード内でのみ、使われます。

この記事が気に入ったら
フォローしてね!

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Pythonが得意。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)