Pythonスキルの習得

【Python】pathlibの特徴と使い方 – 頻繁に使われる操作を厳選 –

【Python】pathlibの特徴と使い方 - 頻繁に使われる操作を厳選 -

こんにちは、Zero-Cheeseです。

本記事では、

  • pathlibモジュールの特徴と
  • よく使うPythonコード

をご紹介しています。

  • Python初心者(最低限の文法をご存知の方)
  • pathlibに初めて触れる方

を対象とした内容となっています。

pathlibの特徴を紹介した後に、頻繁に使用される「Pythonコード」を、ご紹介しました。

pathlibの特徴

既にご存知の方は、「Pythonコードの紹介」の章まで、Skipして下さい。

pathlibとは?

pathlibは、「ファイルパス、ディレクトリパス」の操作を行うパッケージで、Python3.4から追加されました。

ファイルパス、ディレクトリパス操作を、オブジェクトから操作します。

例えば、「./sample.txt」ファイルを削除する場合、

  • osパッケージと、
  • pathlibパッケージの

コードの違いは、下記の通りです。

# osを使用する場合
os.remove("./sample.txt")

# pathlibを使用する場合
p = pathlib.Path("./sample.txt").unlink()
p.unlink()
# もちろん1行で書いてもOK ⇨ pathlib.Path("./sample.txt").unlink()

pathlibは、オブジェクトを使って、処理しています。

「pathlib」使って、何か嬉しい?

「pathlib」で処理できる事は、「os」等を使った処理と同じです。

それでも、「pathlib」を使う事のメリットを、ご紹介します。

  1. パス関係の処理が、1つのパッケージにまとまっている。
  2. パス関係処理が、連続して記述できる

以下、順番に紹介していきます。

パス関係の処理が1つのオブジェクトにまとまっている。

パス関係の処理というのは、

  • ファイル名を変更したり
  • 該当するファイルパスを取得したり

する操作を指します。

これらの操作は、「os」、「os.path」、「glob」等、複数のパッケージに散らばっていましたが、「pathlib」パッケージ1つで、ほぼ網羅できます。

また昨今は、オブジェクト指向が当たり前になっているので、パス関連操作が「オブジェクト」から扱える事も、大きなメリットです。

パス関係の処理が、連続して記述できる

例えば、

  • パスを絶対パスに変換して、
  • その絶対パスに、ファイルパスを追加して、
  • そのファイルが存在するか確認する

という操作を考えてみます。

「pathlib」を使用しないコードと、使用したコードを比較してみます。

# 「pathlib」を使用しないコード
file_path = os.path.abspath('./AAA')
file_path = os.path.join(file_path, 'sample.txt')
print(os.path.exists(file_path))

# 「pathlib」を使用したコード
print(pathlib.Path('./AAA').resolve().joinpath('sample.txt').exists())

上記コードの通り、Javascriptのチェーンメソッド等みたいに、連続して記述する事が可能です。

よく使われるPythonコードをご紹介

「pathlib」は、標準モジュールに含まれているため、追加インストール不要です。(Python3.4以上のみ)

「pathlib」パッケージの読込み

import pathlib

まずは、「Path」オブジェクトの作る

pathlib.Path({パスを指定})

引数に関して:

  • 「絶対パス」、「相対パス」どちらも可(省略時、カレントディレクトリ)
  • パスの最後に、/ を付けても付けなくてもOK(pathlib.Path(‘./sample_dir‘) 、pathlib.Path(‘./sample_dir/‘) 両方OK)

コード例:

p = pathlib.Path('./sample.txt')  
# print(type(p)) を実行すると 
# PosixPath('./sample.txt') が表示される(Macの場合)

pathlib.Pathが返す型(上記コードの、変数 p の型 )

  • UNIX系(Mac等):PosixPath
  • Windows:WindowsPath

(これらは、Pathクラスを継承したクラスのため、本記事では、Pathオブジェクトと表現している箇所があります。)

「ホームディレクトリ」や、「カレントディレクトリ」を指定したい場合、下記方法が使えます。

# 「ホームディレクトリ」を指定したい場合
p = pathlib.Path.home()

# 「カレントディレクトリ」を指定したい場合
p = pathlib.Path.cwd()

「パス」に関連した、確認系の処理

ディレクトリ・ファイルの存在確認

引数に指定した「パス」が、実際に存在するかどうか、確認できます。

(Pathオブジェクト).exists()

  • 存在する場合:True
  • 存在しない場合:False

が返ります。

コード例:

p = pathlib.Path('./sample.txt') 
is_exsits = p.exists()
# 結果: True or False

指定したパスが、ファイル、ディレクトリ、絶対パスなのかを判定

Pathオブジェクトの、引数に指定した「パス」が、

  • ファイルなのか
  • ディレクトリなのか
  • 絶対パスなのかを

確認する事ができます。

  • ファイル判定メソッド: (Pathオブジェクト).is_file()
  • ディレクトリ判定メソッド: (Pathオブジェクト).is_dir()
  • 絶対パス判定メソッド: (Pathオブジェクト).is_absolute()
  • 該当する場合:True
  • 該当しない場合:False

が返ります。

指定したパスに、ファイル/ディレクトリが実際に存在しない場合は、Falseが返ります。

コード例:

# ファイル判定
p = pathlib.Path('./sample.txt') 
is_file = p.is_file()

# ディレクトリ判定
p = pathlib.Path('./sample_dir')
is_dir = p.is_dir()

# 絶対パス判定
p = pathlib.Path('./sample_dir')
is_absolute = p.is_absolute()

配下のディレクトリ/ファイルを取得

全て(ディレクトリ/ファイル 両方含む)、取得する

「Path」オブジェクトのパス配下にある、ディレクトリ/ファイル一覧を取得するメソッドです。

(Pathオブジェクト).iterdir()

ジェネレータが返ります。

コード例:

p = pathlib.Path('sample_dir')
p_iter = p.iterdir()

# list(p_iter)の実行例:
# [PosixPath('sample_dir/sub_dir'),
# PosixPath('sample_dir/3.txt'),
# PosixPath('sample_dir/2.txt'),
# PosixPath('sample_dir/1.txt')]

該当する「ファイル、ディレクトリ」のみ、取得する

(Pathオブジェクト).glob(“{検索条件}”)

引数に関して:

globパッケージのglob関数と同じ使い方です。(例えば、「*」を指定すると、0文字以上の任意の文字列に該当)

ジェネレータが返ります。

コード例:

p = pathlib.Path('./')
p_glob = p.glob("*.txt")

# print(list(p_glob)) を実行した結果(例)
# [PosixPath('sample1.txt'), PosixPath('sample2.txt')]

親ディレクトリの取得

親ディレクトリ一覧を取得する

  • 親ディレクトリ一覧: (Pathオブジェクト).iterdir()

ジェネレータが返ります。(最初に「親のPathオブジェクト」が、次に「親の親」が返ります。)

コード例:

# 例えば、「"/AAA/BBB/CCC"」パスで、オブジェクトを作った場合
p = pathlib.Path('/AAA/BBB/CCC')
p_parents = p.parents

# list(p_iter)の実行:
# [PosixPath('/AAA/BBB'), PosixPath('/AAA'), PosixPath('/')]

1つ上、2つ上の、親ディレクトリを取得する

  • 1つ上の親を取得: (Pathオブジェクト).parents[0]
  • 2つ上の親を取得: (Pathオブジェクト).parents

PosixPath(Unix系)、WindowsPath(windows)型が返ります。

コード例:

# 例えば、「"/AAA/BBB/CCC"」パスで、オブジェクトを作った場合
p = pathlib.Path('/AAA/BBB/CCC')
p_parents0 = p.parents[0]
p_parents1 = p.parents

# print(p_parents0)を実行 ⇨ "/AAA/BBB"
# print(p_parents1)を実行 ⇨ "/AAA"

pathlibの「パス」を変更する処理関係

相対パスを絶対パスに変換

  • (Pathオブジェクト).resolve()

PosixPath(UNIX系)、WindowsPath(Windows)型が返ります。

コード例:

p = pathlib.Path('./sample_dir')
p = p.resolve()
# print(p)を実行 ⇨ (例) "/Users/foo/AAA/sample_dir"

パスのファイル名を変更する

(Pathオブジェクト).with_name({変更したいファイル名})

PosixPath(UNIX系)、WindowsPath(Windows)型が返ります。

コード例:

p = pathlib.Path('AAA/BBB/sample.txt')
p_name = p.with_name('change.txt')
# print(p_name)を実行 ⇨ "AAA/BBB/change.txt"

パスの拡張子のみを変更する

(Pathオブジェクト).with_name({変更したいファイル名})

PosixPath(UNIX系)、WindowsPath(Windows)型が返ります。

コード例:

p = pathlib.Path('AAA/BBB/sample.txt')
p_suffix = p.with_suffix('.png')
# print(p_name)を実行 ⇨ "AAA/BBB/sample.png"

現在のPathオブジェクトに、新たなパスを追加する

(Pathオブジェクト).joint({追加したいパス})

引数に関して:

「追加したいパス」は、可変長引数のため、任意の数の指定が可能です。

また「追加したいパス」は、文字列、Pathオブジェクト、どちらも可能です。(下記コードを参照)

PosixPath(UNIX系)、WindowsPath(Windows)型が返ります。

コード例:

# パスの結合 その1
p = pathlib.Path('AAA')
p_joint = p.joinpath('BBB', 'CCC')
# print(p_joint)を実行 ⇨ "AAA/BBB/CCC"

# パスの結合 その2
p = pathlib.Path('AAA')
p_joint = p.joinpath(pathlib.Path('BBB'), 'CCC', 'DDD')
# print(p_joint)を実行 ⇨ "AAA/BBB/CCC/DDD"

文字列として取得する方法

Pathオブジェクトの「パス」を、文字列で取得

  1. str(Pathオブジェクト)
  2. (Pathオブジェクト).as_posix()

文字列が返ります。

①はUNIX系の場合、POSIX形式(ディレクトリを「/」で区切った方式)が、Windowsの場合、Windowsに対応したパス表示が返ります。(下記コード参照)

②は、POSIX形式(ディレクトリを「/」で区切った方式)で返ります。

コード例:

p = pathlib.Path('/AAA/BBB/CCC')

print(str(p))
# 実行結果
# UNIX系の場合: /AAA/BBB/CCC
# Windowsの場合: \\AAA\\BBB\\CCC

print(p.as_posix())
# 実行結果 (UNIX系、Windows、両方同じ結果)
# /AAA/BBB/CCC

ファイル名、拡張子抜きファイル名、拡張子を取得する

  • ファイル名:(Pathオブジェクト).name
  • 拡張子抜きファイル名:(Pathオブジェクト).stem
  • 拡張子:(Pathオブジェクト).suffix

コード例:

p = pathlib.Path('/AAA/sample.txt')

# ファイル名の取得
print(p.name)
# ⬆ 実行結果:「sample.txt」

# ファイル名(拡張子抜き)の取得
print(p.stem)
# ⬆ 実行結果:「sample」

# 拡張子の取得
print(p.suffix)
# ⬆ 実行結果:「.txt」

Pathオブジェクトの「パス」を、ディレクトリまでのパスと、ファイル名に分離して、取得する

  • os.path.split(Pathオブジェクト)

コード例:

p = pathlib.Path('AAA/BBB/sample.txt')
print(os.path.split(p))
# ⬆ 実行結果
# ('AAA/BBB', 'sample.txt')

ファイル操作(作成、削除など)

空ファイルの作成

  • (Pathオブジェクト).touch(mode=0o666, exist_ok=True)

引数が省略された場合、上記のデフォルト値が設定されます。

引数に関して:

  • mode:ディレクトリに対する権限 (先頭の「0o」は、8進数を表しています。)
  • exist_ok:「True」にすれば、既に該当ファイルがある場合でも、例外(FileExistsError)が発生しない。

コード例:

p = pathlib.Path('test.txt')
p.touch()

# 実行すると、「text.txt」ファイルが作成される。

ファイルの削除

  • (Pathオブジェクト).unlink(missing_ok=False)

引数が省略された場合、上記のデフォルト値が設定されます。

引数に関して:

  • missing_ok:「True」にすれば、既に該当ファイルがある場合でも、例外(FileExistsError)が発生しない。

コード例:

# 標準的な使い方
p = pathlib.Path('test.txt')
p.unlink()
# 実行すると、「text.txt」ファイルが削除

# ファイルが無くても、例外発生しない使い方
p.unlink(True) # 引数に、missing_ok=True でもOK

ファイル操作権限の変更

(Pathオブジェクト).chmod({指定したい権限})

引数には、指定したい権限を、8進数(0oから始まる3桁の数字)で指定します。

コード例:

p = pathlib.Path('sample.txt')
p.chmod(0o666)

# 上記を実行すると、権限が「-rw-rw-rw-」になる。

ファイル名を変更、移動

(Pathオブジェクト).rename({変更後のパス})

引数の使い方は、下記コードをご参照ください。

Pathオブジェクトに、指定したファイルがない場合は、例外(FileNotFoundError)が発生します。

コード例:

# ファイル名のみ、変更したい場合
p = pathlib.Path('./sample.txt')
p.rename('./aaa.txt')

# ファイルを、別ディレクトリに移動したい場合
# 下記の場合、「AAA」ディレクトリが存在しないと、エラー発生
p = pathlib.Path('./sample.txt')
p.rename('./AAA/sample.txt')
# 移動先に、同名のファイルがある場合は、上書き

# 上記2つを組み合わせるのも可能
p = pathlib.Path('./sample.txt')
p.rename('./AAA/aaa.txt')

ディレクトリ操作関係

ディレクトリ名を変更する

ファイル名の変更と、全く同じメソッドで、変更します。

(Pathオブジェクト).rename({変更後のパス})

引数の使い方は、下記コードをご参照ください。

Pathオブジェクトに指定したディレクトリがない場合は、例外(FileNotFoundError)が発生します。

コード例:

# ディレクトリ名の変更
p = pathlib.Path('./AAA') # ディレクトリへのパスを指定
p.rename('./BBB') # 変更したいディレクトリ名を指定

ディレクトリを作成

  • (Pathオブジェクト).mkdir(mode=0o777, parents=False, exist_ok=False)

引数が省略された場合、上記のデフォルト値が設定されます。

引数に関して:

  • mode:ディレクトリに対する権限 (先頭の「0o」は、8進数を表しています。)
  • parents:中間ディレクトリも作成したい場合、引数に「parents=True」を指定
  • exist_ok:「True」にすれば、既に該当ディレクトリがある場合でも、例外(FileExistsError)が発生しない。

コード例:

# 標準的な使い方
p = pathlib.Path('sample_dir')
p.mkdir()
# 実行すると、「sample_dir」ディレクトリが作られる

# 中間ディレクトリも作りたい場合
p = pathlib.Path('sample_dir/sub_dir')
p.mkdir(parents=True)
# 実行すると、「sample_dir」ディレクトリが作られた上に、
#  その配下に、「sub_dir」ディレクトリも作成

ディレクトリを削除

  • (Pathオブジェクト).rmdir()

ディレクトリの中身が空でない場合、例外(OSError)が発生します。

コード例:

p = pathlib.Path('sample_dir')
p.rmdir()

ディレクトリの中身+ディレクトリを全削除

「shutil」パッケージを使用します。

  • shutil.rmtree(‘削除したいディレクトリパス’)

引数に、削除したいディレクトリパスを、文字列で設定します。

コード例:

import shutil
shutil.rmtree('sample_dir')
# 実行結果、ディレクトリも含めて全消去される

最後に

本記事では、「Pathlib」の特徴と使い方を、紹介しました。

ビジネスでコードを作る際は、コード品質を担保する観点からも、「pathlib」の使用が標準になってくると思います。

本記事を通して、使い方を把握して頂けたら、幸いです。

それではまた、お会いしましょう!