Python: 安全・手軽に一時ファイル・一時ディレクトリを作る (tempfile)

一時的に使うファイルやディレクトリを作成して、処理が終わったら削除する、という手続きを実装する機会はしばしばあると思います。

簡単なことではあるのですが、処理の例外ハンドラで削除の実装を忘れてゴミファイルができてしまったり、並列実行したときにプロセス間でファイルパスを競合させてしまったりと、ついハマる落とし穴もあります。

Pythonではこういった一時ファイルを安全・手軽に作成するための標準ライブラリとして tempfile が提供されています。

docs.python.org

$ python --version
Python 3.8.5

一時ファイル

使い方は通常のファイル操作同様シンプルで、open関数の代わりにTemporaryFileオブジェクトを介して読み書きする点だけが異なります。

  • デフォルトモードは "w+b" (mode引数で変更可能)
  • dir引数の指定が無い場合、環境変数TMPDIR / TEMP / TMPのディレクトリ内にこのファイルは生成されます

ポイントは2つで、これによって削除忘れやファイルパス競合を意識する必要がなくなります。

  • closeメソッドを呼び出す or with句内から抜け出す (or ガベージコレクト対象となる) ことで、ファイルも自動的に削除されます
  • TemporaryFileは内部でmkstempを実行しており、重複しないようにランダムな文字列がファイル名に付与されます
import tempfile

f = tempfile.TemporaryFile()
f.write(b"Hello, world!")
print(f.read())
f.close()

# dir引数で出力先ディレクトリを指定できる
with tempfile.TemporaryFile(dir=".") as f:
    f.write(b"Hello, world!")
    f.seek(0)
    print(f.read())

TemporaryFileは不可視のファイルが生成しますが、NamedTempraryFileでは可視ファイルを生成できます。

f = tempfile.NamedTemporaryFile(prefix="a_", suffix="_b", dir=".")
f.write(b"Hello, world!")

# 出力された一時ファイルの確認
from glob import glob
print(glob("./a_*_b")[0])  # ./a_z91g49js_b

これら2つに加えて、指定のサイズとなるまでメモリにスプールするSpooledTemporaryFileというのもあります。

一時ディレクトリ

一時ディレクトリはTemporaryDirectoryで作成できます。以下のように、コンテキストマネージャ (with句) 内だけでアクセスできるディレクトリが生成され、抜け出すときには削除されます。

import os

with tempfile.TemporaryDirectory(prefix="tmp_", dir=".") as dirpath:
    print(dirpath)  # ./tmp_xo0tg2u1

    with open(os.path.join(dirpath, "a.txt"), "w") as f:
        f.write("Hello, world!")
    print(glob(os.path.join(dirpath, "*")))  # ['./tmp_xo0tg2u1/a.txt']

print(glob("./tmp_*"))  # [] (削除済み)