0
1

Pythonのクラスされた生成されたオブジェクトが破棄されるタイミング:オブジェクトの寿命とガーベジコレクションの真実

Posted at

Pythonオブジェクトの寿命:スコープとガーベジコレクションの真実

Pythonでクラスからオブジェクトを作るとき、「スコープから外れたら消える」と思っていませんか? 実は、必ずしもそうとは限りません! オブジェクトの寿命を握るのは 参照カウントガーベジコレクション です。

スコープから抜けたら破棄されるんじゃないの?

一見、スコープとオブジェクトの寿命は関係ありそうですが…
以下は、スコープを抜けた場合のオブジェクト破棄の単純な例です。

class SomeClass:
    def __init__(self):
        print("Instance created")

    def __del__(self):
        print("Instance destroyed")

def func():
    obj = SomeClass()  # SomeClassのオブジェクトを作成
    print("Inside the function")

func()
# "Instance created"
# "Inside the function"
# スコープを抜けたため、オブジェクトも破棄される
# "Instance destroyed"

上の例ではスコープから抜けたためにオブジェクトが破棄されるようにみえます...

参照カウントとガーベジコレクションの舞台裏

実は、オブジェクトは 「参照カウント」という値を持っています。これは、そのオブジェクトを「参照している」変数やデータ構造の数です。

参照カウントが 0 になったら、オブジェクトはもはや必要ないと判断され、 ガーベジコレクション によってメモリから解放されます。以下は参照カウントを追跡した例です。

import sys

class SomeClass:
    def __init__(self):
        print("Instance created")

    def __del__(self):
        print("Instance destroyed")

obj = SomeClass()
print(sys.getrefcount(obj))  # 出力: 2 (objとsys.getrefcountの引数として渡されているため)

another_ref = obj
print(sys.getrefcount(obj))  # 出力: 3 (another_refが新たに参照)
print(sys.getrefcount(another_ref))  # 出力: 3

del another_ref
print(sys.getrefcount(obj))  # 出力: 2 (another_refを削除)

del obj
# "Instance destroyed" (objが削除され参照カウントが0になる)

参照カウントが 0 になったら、オブジェクトはもはや必要ないと判断され、 ガーベジコレクション によってメモリから解放されます。

さよならオブジェクト! __del__メソッドで最後の挨拶

オブジェクトが消える瞬間に何か処理を実行したい場合は、 __del__メソッドを定義します。ただし、__del__メソッドの呼び出されるタイミングは、ガベージコレクタ次第です。

class ResourceManager:
    def __init__(self):
        self.resource = "Important data"
        print("Resource acquired")
    
    def __del__(self):
        print(f"Releasing resource: {self.resource}")

# オブジェクトの作成と破棄
rm = ResourceManager()
del rm
# この時点で"Releasing resource: Important data"が出力されるかもしれないが、保証はない

print("Program continuing...")
# プログラムの終了時に__del__が呼ばれる可能性もある

__del__メソッドの呼び出しのタイミングは保証されません ! リソースの解放などの重要な操作に使用すべきではありません。その代わりに、with文を使用することをお勧めします。

with文との比較: リソース管理のより良い方法

__del__ メソッドは、オブジェクト消滅時の処理を定義できますが、ガーベジコレクションのタイミングは予測できません。ファイルやネットワークなど、確実にリソースを解放したい 場合は、 with文 を使いましょう。

class DatabaseConnection:
    def __init__(self):
        print("Connecting to database")
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Closing database connection")
    
    def query(self):
        print("Executing query")

# with文を使用した場合
with DatabaseConnection() as db:
    db.query()
# ここで確実にデータベース接続が閉じられる

print("After with block")

# with文を使用しない場合
db = DatabaseConnection()
db.query()
# ここでデータベース接続が閉じられる保証はない

print("After normal usage")

上記の例では、withブロックに入ったときに__enter__メソッドが呼ばれ、ブロックを抜けるときに__exit__メソッドが呼ば出され、リソースが解放されます。これにより、例外が発生した場合でもリソースの適切な解放が保証されます。

まとめ

Pythonのオブジェクトの寿命は、スコープだけでなく、参照カウントとガーベジコレクションによって管理されています。 __del__ メソッドで最後の処理を定義することもできますが、リソースの確実な解放にはwith文を使いましょう!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1