プログラム終了時に呼び出される関数の登録方法
- atexit.register() メソッドを使用して、プログラム終了時に呼び出したい関数を登録します。
- 関数に引数が必要な場合は、atexit.register() メソッドの第二引数以降にそれらを渡します。
- 複数の関数を登録することが可能で、最後に登録された関数から順に呼び出されます。
import atexit
atexit.register(<プログラム終了時に実行する関数>)
atexit.register(<プログラム終了時に実行する関数>, <関数に渡す引数>)
atexit.register(<プログラム終了時に実行する関数>, <関数に渡す引数1>, <関数に渡す引数2>,...)
サンプルコード1
次のサンプルコードは atexit.register() メソッドを使用して2つの関数を登録しています。最後に登録された関数から順に呼び出されるため、メッセージは「2.プログラム終了」「1.プログラム終了」の順で表示されます。import atexit
# プログラム終了時に呼びたい関数
def cleanup1():
print('1.プログラム終了')
# プログラム終了時に呼びたい関数
def cleanup2():
print('2.プログラム終了')
# プログラム終了時に呼び出される関数を登録
atexit.register(cleanup1)
atexit.register(cleanup2)
サンプルコード2
次のサンプルコードは、関数の登録時に2つの引数を渡しています。関数は 2つの引数を結合して表示するため、「2.プログラム終了」「1.プログラム終了」の順で表示されます。import atexit
# プログラム終了時に呼びたい関数
def cleanup(message1, message2):
print(message1 + message2)
# 2つの引数とともに、プログラム終了時に呼び出される関数を登録
atexit.register(cleanup, '1.', 'プログラム終了')
atexit.register(cleanup, '2.', 'プログラム終了')
デコレータを使用して関数を登録する
関数を登録するためのデコレータも用意されています。プログラム終了時に呼び出したい関数にデコレータ @atexit.register を指定します。- デコレータを使用する場合、引数は渡せません。
- 複数の関数にデコレータ付けると、最後に定義された関数から順に呼び出されます(ファイル内の、下から順番に呼ばれる)。
import atexit
@atexit.register
def <関数名>():
<終了時に実行したい処理>
サンプルコード
次のサンプルコードは、デコレータを使用してプログラム終了時に呼び出す関数を登録しています。最後に定義された関数から順に呼び出されるため、「2.プログラム終了」「1.プログラム終了」の順で表示されます。import atexit
@atexit.register
def cleanup1():
print('1.プログラム終了')
@atexit.register
def cleanup2():
print('2.プログラム終了')
登録した関数の削除
atexit.unregister() メソッドの引数に、削除したい関数を渡します。import atexit
atexit.unregister(<削除する関数>)
- 登録されていない関数を渡した場合、何も起こりません。
- 同名の関数が複数登録されているとき、1回の削除で全て削除されます。
サンプルコード
次のサンプルコードでは、atexit.unregister() メソッドを使用して、atexit.register() で登録した関数を削除しています。登録が2回に対し、削除は1回行われています。しかし、同名の関数は全て削除されるため、結果として何も表示されません。import atexit
def cleanup(message1, message2):
print(message1 + message2)
# 2つの引数とともに、プログラム終了時に呼び出される関数を登録
atexit.register(cleanup, '1.', 'プログラム終了')
atexit.register(cleanup, '2.', 'プログラム終了')
# 登録した関数を削除
atexit.unregister(cleanup)
登録した関数内で例外が発生した場合
リソースのクリーンアップや重要な終了処理を確実に行うため、atexit.register() で登録された関数は、例外が発生しても全て呼び出されます。サンプルコード
次のサンプルコードでは、cleanup2() -> cleanup1() の順に実行されますが、cleanup2() 内で例外を発生させています。 例外が発生しても全ての関数が呼び出されるため、'プログラム終了' のメッセージも表示されます。import atexit
@atexit.register
def cleanup1():
print('プログラム終了')
@atexit.register
def cleanup2():
# 例外を発生させる
raise ValueError("例外発生")
サンプルプログラムの実行結果
D:\test>python sample-cleanup-raise.py
Exception ignored in atexit callback: <function cleanup3 at 0x0000021FE8BFD080>
Traceback (most recent call last):
File "D:\test\sample-cleanup-raise.py", line 17, in cleanup3
raise ValueError("2.error")
ValueError: 2.error
Exception ignored in atexit callback: <function cleanup2 at 0x0000021FE8BF0EA0>
Traceback (most recent call last):
File "D:\test\sample-cleanup-raise.py", line 12, in cleanup2
raise ValueError("1.error")
ValueError: 1.error
プログラム終了

登録した関数が呼ばれないケース
通常、登録された関数は sys.exit()、exit()、quit() が呼び出されたとき、あるいは、メインモジュールの実行が完了したときに呼ばれます。しかし、次のようなケースでは呼ばれません。-
os._exit() が呼び出された場合
os._exit()メソッドは、プログラムを即座に終了させます。通常の終了処理が行われないため、atexitで登録した関数も呼び出されません。
-
kill などで Python が強制終了された場合
killコマンドなどを使用してプロセスが強制終了された場合、プログラムは通常の終了処理を行う機会を得られません。したがってatexitで登録した関数も呼び出されません。
-
致命的なエラーが検出された場合
Pythonが致命的なエラー(例えばメモリアクセス違反)を検出した場合、プログラムは即座に終了します。このような状況では、atexitで登録した関数は呼び出されません。
参考資料
- Python公式ドキュメント - atexit --- 終了ハンドラ
- Python公式ドキュメント - decorator
- Python公式ドキュメント - sys.exit([arg])
- Python公式ドキュメント - exit(code=None)
- Python公式ドキュメント - quit(code=None)
- Python公式ドキュメント - os._exit(n)
検証環境
- Python 3.11.3 (tags/v3.11.3:f3909b8, Apr 4 2023, 23:49:59) [MSC v.1934 64 bit (AMD64)] on win32
- Microsoft Windows 10 Enterprise Version 22H2 OS Build 19045.3693 Experience: Windows Feature Experience Pack 1000.19053.1000.0