pathlibモジュールが提供するPath.renameメソッドを使って、ファイルやディレクトリの名前を変更したり、移動したりする方法を紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
from pathlib import Path
# ファイルの名前変更
foo = Path('foo.txt')
foo.touch()
bar = Path('bar.txt')
bar.touch()
# 存在しないファイルの名前に変更
baz = foo.rename('baz.txt')
print(baz) # baz.txt:renameメソッドは変更後のファイルのパスを返す
# 既存のファイルの名前に変更
tmp = baz.rename(bar) # UNIX:OK、Windows:FileExistsError
# ファイルの移動
a = Path('a')
a.mkdir()
a_bar = bar.rename('a/bar.txt')
#a_bar = bar.rename(Path(a, bar))
a_bar.rename('a/b/bar.txt') # FileNotFoundError
# ディレクトリの名前変更
dir0 = Path('dir1/dir2/dir3')
dir0.mkdir(parents=True)
dir1 = Path('dir1')
dir4 = dir1.rename('dir4')
# 既存ディレクトリの名前に変更しようとした場合
dir5 = Path('dir5')
dir5.mkdir()
dir5 = dir4.rename(dir5) # Windows:FileExistsError
# 空でないディレクトリの名前に変更しようとした場合
dir6 = Path('dir6')
dir7 = Path(dir6, 'dir7')
dir7.mkdir(parents=True)
dir5.rename(dir6) # UNIX:OSError、Windows:FileExistsError
# ディレクトリの移動
dir5.rename('dir6/dir7/dir5')
ファイルやディレクトリの名前を変更したり、別のディレクトリに移動したりするには幾つかの方法がある。
本稿では、このうちpathlibモジュールを使用する方法を紹介する。
pathlibモジュールで定義されているPathクラスのインスタンス(コードを実行するプラットフォームに応じて、通常はPosixPathクラスのインスタンスかWindowsPathクラスのインスタンスのどちらかとなる)にはrenameメソッドがある。このメソッドの構文を以下に示す。
from pathlib import Path
p = Path(……)
target = Path(……)
p.rename(target)
引数は1つだけで、そのオブジェクトが表しているパスの「新しい名前となる文字列かPathオブジェクト」を指定する。新しい名前として、ディレクトリを含む文字列またはPathオブジェクトを渡せば、そのファイル/ディレクトリが移動することになる。戻り値は、変更後のパスを表すPathオブジェクトとなる。
このメソッドは内部的にはosモジュールで定義されているrename関数を使用しているので(pathlib.pyのソースコードを参照)、その振る舞いもos.rename関数と同様だ。上の「p.rename(target)」は実質的には「os.rename(p, target)」と等価である。
os.rename関数を内部で使用しているので、UNIX系統のOSとWindowsでは振る舞いが異なる。Windowsではtargetに既存のファイルやディレクトリを指定すると常にFileExistsError例外が発生する。
移動元 | 移動先 | UNIX | Windows |
---|---|---|---|
ファイル | 存在しない | 成功 | 成功 |
ファイル | ファイル | 成功 | FileExistsError |
ファイル | ディレクトリ | IsADirectoryError | FileExistsError |
ディレクトリ | 存在しない | 成功 | 成功 |
ディレクトリ | ファイル | NotADirectoryError | FileExistsError |
ディレクトリ | ディレクトリ(空) | 成功 | FileExistsError |
ディレクトリ | ディレクトリ(空ではない) | OSError | FileExistsError |
Path.renameインスタンスメソッドの動作 |
以下に簡単な例を示す。
from pathlib import Path
foo = Path('foo.txt')
foo.touch()
bar = Path('bar.txt')
bar.touch()
baz = foo.rename('baz.txt')
print(baz) # baz.txt:renameメソッドは変更後のファイルのパスを返す
この例では、fooとbarという2つのPathオブジェクトがあり、カレントディレクトリにあるfoo.txtファイルとbar.txtをそれぞれが表している(実際、touchメソッドを呼び出してそれらのファイルを作成している)。
そして、fooオブジェクトのrenameメソッドを呼び出して、このオブジェクトが表しているファイル(foo.txt)の名前を「baz.txt」に変更している。ここではPathオブジェクトではなく文字列をrenameメソッドに渡している。また、戻り値は、変更後のファイル「baz.txt」を表すPathオブジェクトなので、print関数にこれを渡すと「baz.txt」と表示される。
既に述べたが、既存ファイルの名前をtargetに指定した場合、UNIX系統のOSでは既存ファイルは上書きされる。WindowsではFileExistsError例外が発生する。上の例ではfoo.txtとbar.txtの2つのファイルを作成して、foo.txtの名前をbaz.txtに変更した。ここで、baz.txtの名前をbar.txtに変更してみよう。
tmp = baz.rename(bar) # UNIX:OK、Windows:FileExistsError
この例では、先ほどの「baz = foo.rename('baz.txt')」行で得られた、名前変更後のファイルのパスを表すオブジェクトbazを使って、renameメソッドを呼び出している。引数には、文字列ではなく、先ほど作成したbar.txtファイルを表すbarオブジェクトを渡している点にも注意しよう。
UNIXではこれは成功してファイル名が変更されるが、WindowsではFileExistsError例外が発生する。以下はWindows上で動作するVisual Studio Code(以下、VS Code)から上記のコードを実行したところだ。
変更後の名前としてtargetに既存ディレクトリの名前を指定すると、例外の種類は異なるがUNIXでもWindowsでもエラーとなる。
ファイルを移動するには、targetにディレクトリを含んだパスを文字列かPathオブジェクトの形で指定する。以下に例を示す。
a = Path('a')
a.mkdir()
a_bar = bar.rename('a/bar.txt')
#a_bar = bar.rename(Path(a, bar))
この例では、カレントディレクトリに「a」というディレクトリを作成し、そこにbar.txtファイルを移動している。最後にあるコメントアウトされた行を見ると分かるが、aディレクトリを表すPathオブジェクト、bar.txtファイルを表すbarオブジェクトから作成したPathオブジェクト(これはa/bar.txtを表す)を渡すといったことも可能だ。
targetに指定した文字列やPathオブジェクトが存在しないディレクトリを含んでいる場合には例外が発生する。
a_bar.rename('a/b/bar.txt') # FileNotFoundError
これは先の例でaディレクトリに移動したb.txtファイル、つまりa/b.txtを表すオブジェクトをaディレクトリの下にあるbディレクトリの下に移動しようとしている。しかし、a/bディレクトリはないのでFileNotFoundError例外が発生している。
Copyright© Digital Advantage Corp. All Rights Reserved.