<一覧に戻る

オブジェクトの参照とリファレンスカウントの増減

Pythonは、メモリ管理のためにリファレンスカウントという仕組みを使用しています。これは、オブジェクトがどれだけ参照されているかを追跡する方法です。この教材では、オブジェクトの参照とリファレンスカウントの増減について学び、実際にコードを使ってその動作を確認します。

リファレンスカウントの基本

リファレンスカウントは、各オブジェクトがどれだけの参照を持っているかを示す整数値です。このカウントがゼロになると、そのオブジェクトはメモリから解放されます。以下のコードを使って、リファレンスカウントがどのように増減するかを見てみましょう。

サンプルコード

import sys

# 新しいオブジェクトを生成
a = []  # 空のリストを作成
print(f'リスト a のリファレンスカウント: {sys.getrefcount(a)}')

# リファレンスカウントを増やす
b = a  # a を b に代入
print(f'リスト a のリファレンスカウント: {sys.getrefcount(a)}')

# リファレンスカウントを減らす
del b  # b を削除
print(f'リスト a のリファレンスカウント: {sys.getrefcount(a)}')

コード解説

  1. import sys

    • Pythonのsysモジュールをインポートします。
    • sysモジュールにはsys.getrefcount()という関数が含まれ、引数として渡したオブジェクトの現在の参照カウント(リファレンスカウント)を取得できます。
  2. a = []

    • 空のリストオブジェクトを作成し、変数aに代入します。
    • この時点で、リストaは少なくとも1つの参照を持っています(a自体の参照)。
  3. sys.getrefcount(a)

    • sys.getrefcount(a)は、リストaのリファレンスカウントを返します。
    • ただし、関数呼び出し自体が一時的な参照を生成するため、実際のリファレンスカウントよりも常に1多い値を表示します。
  4. b = a

    • 変数bに変数aを代入すると、リストaへの参照が1つ増えます。
    • この状態で、リファレンスカウントが1増加した結果を確認できます。
  5. del b

    • 変数bを削除すると、リストaへの参照が1つ減ります。
    • 結果として、リストaのリファレンスカウントが1減少します。

参照の増減の実際の影響

次に、リファレンスカウントがどのようにオブジェクトの寿命に影響を与えるかを見てみましょう。以下のコードを実行してみてください。

import sys

class MyClass:
    def __init__(self, name):
        self.name = name

obj1 = MyClass("Object 1")
print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')

obj2 = obj1  # 参照を増やす
print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')

obj3 = obj1  # さらに参照を増やす
print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')

del obj2  # 参照を減らす
print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')

del obj3  # さらに参照を減らす
print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')

# obj1 を削除する前にリファレンスカウントを確認
print(f'obj1 の最終リファレンスカウント: {sys.getrefcount(obj1)}')

del obj1  # 最後の参照を削除

# obj1 を削除した後にアクセスしないようにする
# print(f'obj1 のリファレンスカウント: {sys.getrefcount(obj1)}')  # これはコメントアウトまたは削除

コード解説

  1. class MyClass

    • クラス MyClass を定義しています。初期化メソッド __init__ でオブジェクトに名前を設定します。
  2. obj1 = MyClass("Object 1")

    • クラス MyClass のインスタンスを生成し、変数 obj1 に代入します。
    • この時点で、obj1 による参照が1つ作成されますが、sys.getrefcount 関数が呼び出されたときの一時参照も加わるため、リファレンスカウントは 2 になります。
  3. obj2 = obj1obj3 = obj1

    • obj1 を他の変数 obj2obj3 に代入することで、リファレンスカウントがそれぞれ1ずつ増加します。
    • リファレンスカウントは最終的に 4 になります(obj1, obj2, obj3, 一時参照)。
  4. del obj2del obj3

    • obj2obj3 を削除すると、obj1 のリファレンスカウントがそれぞれ1ずつ減少します。
    • 参照が削除されるたびに、リファレンスカウントが更新されます。
  5. 最終確認とdel obj1

    • 最後に obj1 を削除することで、リファレンスカウントがゼロになり、オブジェクトがメモリから解放されます。

まとめ

リファレンスカウントは、Pythonのメモリ管理において非常に重要な役割を果たしています。オブジェクトの参照が増減することで、その寿命が決まります。次回は、リファレンスカウントによるメモリ解放の仕組みについて学んでいきましょう。

出力結果: