atexit --- 終了ハンドラー¶
atexit モジュールは、終了ハンドラー を登録・解除する関数を定義します。
終了ハンドラーは、プログラムの正常終了時 (例えば sys.exit() が呼び出されたときや、メインモジュールの実行が完了したとき)、より一般的には interpreter shutdown 時に自動的に実行される関数です。
終了時には、登録されているすべての終了ハンドラーを登録した順序の 逆順 に呼び出します。
A、B、C の順に登録すると、インタープリタの終了時には C、B、A の順に実行します。
これは、通常は低水準のモジュールが高水準のモジュールより先にインポートされるので、後片付けは後で行う必要があるという想定に基づいています。
If an exception is raised during execution of an exit handler, a traceback is
printed (unless SystemExit is raised) and the exception information is
saved. After all exit handlers have had a chance to run, the last exception to
be raised is re-raised.
複数のインタープリタを使うプログラムでは、各インタープリタが終了ハンドラーのスタックを持ちます。
ハンドラーは、そのインタープリタが終了するとき (例えば concurrent.interpreters.Interpreter.close() や C API の Py_EndInterpreter() による終了時) に実行されます。
このモジュールの登録用関数が影響するのは、呼び出し元のインタープリタだけです。
注意: Python が処理しないシグナルによってプログラムが強制終了した場合、Python 内部の致命的なエラーが検出された場合、または os._exit() が呼び出された場合、終了ハンドラーは呼び出されません。
注意: クリーンアップ関数内からの関数の登録・登録解除効果は定義されていません。
警告
終了ハンドラーを作成するとき、特に C API を使った拡張では、自身の後片付けが終わった後も、他の終了ハンドラーが任意の Python コードを実行する可能性があることに注意してください。 そのようなコードは正常に実行されるか例外で失敗するべきであり、クラッシュしてはいけません。
バージョン 3.12 で変更: 終了ハンドラー内で新しいスレッドを開始したり、os.fork() で新しいプロセスを作成したりしようとすると、RuntimeError を送出するようになりました。
以前は、Python ランタイムのメインスレッドがスレッド状態を解放する一方で、threading 内部のルーチンや新しいプロセスがその状態を使おうとして、競合状態が発生する可能性がありました。
その結果、正常に終了せずクラッシュすることがありました。
バージョン 3.7 で変更: サブインタープリタで使う場合、登録した関数は登録先のインタープリタに固有となりました。
- atexit.register(func, *args, **kwargs)¶
func を終了ハンドラーとして登録します。 func に渡す引数があれば、
register()の引数として渡してください。 同じ関数と引数を複数回登録することもできます。この関数は func を返し、これをデコレータとして利用できます。
- atexit.unregister(func)¶
終了ハンドラーのリストから func を削除します。 func が登録されていなければ、
unregister()は何もせず、エラーも出しません。 func が複数回登録されていた場合、atexitの呼び出しスタックから、その関数をすべて削除します。 登録解除では内部で等価比較 (==) を使うため、関数の参照先が同一のオブジェクトである必要はありません。
atexit の使用例¶
次の簡単な例では、モジュールのインポート時にファイルからカウンタの値を読み込んで初期化し、プログラムの終了時に更新後の値を自動保存する方法を示します。 アプリケーションが終了時にこのモジュールを明示的に呼び出す必要はありません。
try:
with open('counterfile') as infile:
_count = int(infile.read())
except FileNotFoundError:
_count = 0
def incrcounter(n):
global _count
_count = _count + n
def savecounter():
with open('counterfile', 'w') as outfile:
outfile.write('%d' % _count)
import atexit
atexit.register(savecounter)
register() に指定した位置引数とキーワード引数は登録した関数を呼び出す際に渡されます:
def goodbye(name, adjective):
print('Goodbye %s, it was %s to meet you.' % (name, adjective))
import atexit
atexit.register(goodbye, 'Donny', 'nice')
# または:
atexit.register(goodbye, adjective='nice', name='Donny')
デコレータ として利用する例:
import atexit
@atexit.register
def goodbye():
print('You are now leaving the Python sector.')
デコレータとして利用できるのは、引数なしで呼び出せる関数に限られます。