- Pathライブラリの使い方が知りたい!
このような方に向けて書きました。
Python でパス操作するときは os
ライブラリを使う方が多いかもしれません。
ところが Python 3.4 から標準ライブラリとして搭載されてからは、Pathlib
ライブラリが使われることも徐々に増えてきました。
例えば Python の Web フレームワーク Django でも、バージョン2.0からは os の代わりに pathlib が使われています。
pathlib
はos
を完全に置き換えるものではありませんが、よりシンプルにパスを表現できるので圧倒的に便利です。
この 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())
ディレクトリが空かどうか確認
ディレクトリが空かどうかを判定する直接のメソッドはないので、次のように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_empty
がTrue
であればディレクトリは空であることが分かります。
ディレクトリ一覧を取得
ディレクトリ一覧を取得するには、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()
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} はファイルではありません。")
拡張子を変更
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)
ディレクトリを移動
ディレクトリの移動は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
を返すことができます。
コメント