【Python】デストラクタはプログラマの強い味方です

2023 年 7 月 31 日 by tomokiy

デストラクタとは

言うまでもなく、コンストラクタの反対の役割を担うものです。

クラスインスタンスが破棄される際に必ず実行されます。 例外処理とは関係ないですが、tryexceptの例外処理のfinally句と似ています。 なので、何があってもクラスの終了時に必ず実行してほしいものを記述します。

どういう動きになっているか

以下のようなプログラムを実行してみると分かりやすいです。

class Test1:
    def __init__(self) -> None:
        print("Test1のコンストラクタを実行しました。")
        test2 = Test2()
    def __del__(self) -> None:
        print("Test1のデストラクタを実行しました。")
​
class Test2:
    def __init__(self) -> None:
        print("Test2のコンストラクタを実行しました。")
    def __del__(self) -> None:
        print("Test2のデストラクタを実行しました。")
​
test = Test1()

実行結果は以下の通りです。

$ python test.py
Test1のコンストラクタを実行しました。
Test2のコンストラクタを実行しました。
Test2のデストラクタを実行しました。  
Test1のデストラクタを実行しました。

Test1クラスのインスタンスを作成し、Test1クラスの中でさらにTest2クラスのインスタンスも作成しています。

Test2クラスのインスタンスtest2はTest1クラスのインスタンス変数ではないためデストラクタ処理時に不要です。そのため、Test2クラスのデストラクタが先に呼ばれます。

次の場合はどうでしょう?test2をTest1クラスのインスタンス変数にしてみました。

class Test1:
    def __init__(self) -> None:
        print("Test1のコンストラクタを実行しました。")
        self.test2 = Test2()
    def __del__(self) -> None:
        print("Test1のデストラクタを実行しました。")
​
class Test2:
    def __init__(self) -> None:
        print("Test2のコンストラクタを実行しました。")
    def __del__(self) -> None:
        print("Test2のデストラクタを実行しました。")
​
test = Test1()

実行結果は以下の通りです。

$ python test.py
Test1のコンストラクタを実行しました。
Test2のコンストラクタを実行しました。
Test1のデストラクタを実行しました。
Test2のデストラクタを実行しました。

この場合、Test1クラスのインスタンス変数test2はデストラクタでも使用される可能性があるため、まだ破棄されません。そのため、Test1クラスのデストラクタが先に呼ばれます。

では例外が起きた場合はどうでしょう?デストラクタは実行されるのでしょうか?

class Test1:
    def __init__(self) -> None:
        print("Test1のコンストラクタを実行しました。")
        self.test2 = Test2()
        raise Exception # 例外が発生
    def __del__(self) -> None:
        print("Test1のデストラクタを実行しました。")
​
class Test2:
    def __init__(self) -> None:
        print("Test2のコンストラクタを実行しました。")
    def __del__(self) -> None:
        print("Test2のデストラクタを実行しました。")
​
test = Test1()

実行結果は以下の通りです。

$ python test.py
Test1のコンストラクタを実行しました。
Test2のコンストラクタを実行しました。
Traceback (most recent call last):
  File "C:\Users\tomokiy\Desktop\test.py", line 15, in <module>
    test = Test1()
  File "C:\Users\tomokiy\Desktop\test.py", line 5, in __init__
    raise Exception
Exception
Test1のデストラクタを実行しました。
Test2のデストラクタを実行しました。

はい。例外が発生してもデストラクタは実行されます。

なので、クラス内で例外が発生した場合にプログラム終了のタイミングで何かさせたい場合は、デストラクタに記述したほうが、いちいち全てのメソッド内のfinally句に同じ処理を記述する必要がなくなります。ただし、デストラクタはプログラム終了時に動くのであって、例外が発生した直後に動くものではないため、クラスが破棄されるまでに実行したいことはfinally句に記述しましょう。

まとめ

デストラクタはどんな時でもプログラム終了時に必ず実行してくれる、プログラマの強い味方です。 ぜひデストラクタを実装して質の良いコードにしていきましょう。

タグ:

TrackBack