Pythonのid()関数を徹底解説!オブジェクトのユニークな識別子を理解する


 

Pythonでプログラミングをしていると、「この変数とあの変数は、同じものを指しているのだろうか?」「オブジェクトがメモリ上のどこに存在しているのかを知りたい」といった、オブジェクトの同一性に関する疑問を持つことがあります。特に、ミュータブル(変更可能)なオブジェクトとイミュータブル(不変)なオブジェクトの挙動を理解する上で、この「同一性」は非常に重要になります。このようなときに役立つのが、Pythonの組み込み関数である**id()関数**です。この記事では、id()関数の基本的な使い方から、その意味、そしてオブジェクトの同一性に関する重要な概念までを初心者にもわかりやすく解説しますします。

 

id()関数とは?Pythonにおけるオブジェクトのユニークな識別子

 

Pythonのid()関数は、引数として渡されたオブジェクトの**「識別値(identity)」**を返す組み込み関数です。この識別値は、オブジェクトの生存期間中において一意であり、通常はオブジェクトがメモリ上に格納されているアドレス(メモリアドレス)に対応します。

 

基本的な使い方

 

id()関数は、任意のオブジェクトを引数に取ります。

Python
 
# 整数のID
num1 = 10
num2 = 10
num3 = 20

print(f"num1のID: {id(num1)}") # 出力例: num1のID: 140737352329296
print(f"num2のID: {id(num2)}") # 出力例: num2のID: 140737352329296 (num1と同じ)
print(f"num3のID: {id(num3)}") # 出力例: num3のID: 140737352329616 (異なる)

# 文字列のID
str1 = "hello"
str2 = "hello"
str3 = "world"

print(f"str1のID: {id(str1)}") # 出力例: str1のID: 1944889709296
print(f"str2のID: {id(str2)}") # 出力例: str2のID: 1944889709296 (str1と同じ)
print(f"str3のID: {id(str3)}") # 出力例: str3のID: 1944889713520 (異なる)

# リストのID
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1 # list3はlist1への参照

print(f"list1のID: {id(list1)}") # 出力例: list1のID: 1944889721728
print(f"list2のID: {id(list2)}") # 出力例: list2のID: 1944889721984 (list1と異なる)
print(f"list3のID: {id(list3)}") # 出力例: list3のID: 1944889721728 (list1と同じ)

注意点: id()が返す具体的な数値は、実行環境(Pythonのバージョン、OS、メモリの状態など)によって異なります。重要なのは、その数値が同じか異なるかという点です。

 

is演算子との関係

 

Pythonには、2つの変数が**同じオブジェクトを指しているか(同一であるか)**をチェックするためのis演算子があります。これは内部的にid()関数の結果を比較していると考えることができます。

Python
 
num1 = 10
num2 = 10
num3 = 20

print(f"num1 is num2: {num1 is num2}") # 出力: True (IDが同じだから)
print(f"num1 is num3: {num1 is num3}") # 出力: False (IDが異なるから)

list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = list1

print(f"list1 is list2: {list1 is list2}") # 出力: False (値は同じだが、別のオブジェクト)
print(f"list1 is list3: {list1 is list3}") # 出力: True (同じオブジェクトを参照している)

is演算子はオブジェクトの同一性を、==演算子はオブジェクトの等価性(値が同じかどうか)をチェックします。

 

id()関数とオブジェクトの同一性、ミュータブル/イミュータブル

 

id()関数は、Pythonのオブジェクトモデル、特にミュータブル(変更可能)とイミュータブル(不変)の挙動を理解する上で非常に重要です。

 

イミュータブル(不変)なオブジェクトの挙動

 

整数、浮動小数点数、文字列、タプル、frozensetなどはイミュータブルなオブジェクトです。一度作成されると、その内容を変更することはできません。内容を変更しようとすると、実際には新しいオブジェクトが作成され、変数がその新しいオブジェクトを参照することになります。

Python
 
# イミュータブルな整数
a = 5
print(f"aの初期ID: {id(a)}") # 出力例: aの初期ID: ...

a = a + 1 # aを6に変更
print(f"a変更後のID: {id(a)}") # 出力例: a変更後のID: ... (新しいID、元の5のオブジェクトは残っている)

上記の例では、a = a + 1という操作で、aが指すオブジェクトが5から6に変わったのではなく、6という新しい整数オブジェクトが作成され、aはその新しいオブジェクトを指すようになったことをid()が示しています。

 

文字列のインタニング(Interning)

 

短い文字列や頻繁に使われる整数(-5から256まで)などは、Pythonの最適化(インタニング)により、同じ値であれば同じオブジェクトを参照する場合があります。これはパフォーマンス向上のためのもので、特に短い文字列でよく見られますが、常に保証されるわけではありません。

Python
 
s1 = "python"
s2 = "python"
s3 = "py" + "thon" # 式の結果が同じ値になる
print(f"s1のID: {id(s1)}")
print(f"s2のID: {id(s2)}") # s1と同じIDになる可能性が高い
print(f"s3のID: {id(s3)}") # s1, s2と同じIDになる可能性が高い

 

ミュータブル(変更可能)なオブジェクトの挙動

 

リスト、辞書、セット、bytearrayなどはミュータブルなオブジェクトです。これらのオブジェクトは、内容を変更してもオブジェクト自体のIDは変わりません。つまり、メモリ上の同じ場所にあるオブジェクトが変更されます。

Python
 
# ミュータブルなリスト
my_list = [1, 2, 3]
print(f"my_listの初期ID: {id(my_list)}") # 出力例: my_listの初期ID: ...

my_list.append(4) # リストの内容を変更
print(f"my_list変更後のID: {id(my_list)}") # 出力例: my_list変更後のID: ... (初期IDと同じ)
print(f"my_list: {my_list}") # 出力: my_list: [1, 2, 3, 4]

この挙動は、関数にミュータブルなオブジェクトを渡した場合に、関数内でそのオブジェクトが変更されると、呼び出し元の変数もその変更を反映することにつながります。

 

id()関数の活用事例

 

 

1. デバッグ時におけるオブジェクトの追跡

 

複雑なコードベースで、特定のオブジェクトがいつ、どこで変更されたか、あるいは意図せずコピーされてしまったのかなどをデバッグする際に役立ちます。

Python
 
data = [1, 2, 3]
print(f"Step 1 ID: {id(data)}")

def modify_list(items):
    print(f"Inside function ID: {id(items)}")
    items.append(4) # ミュータブルな変更

modify_list(data)
print(f"Step 2 ID: {id(data)}") # Step 1と同じID
print(f"Modified data: {data}")

 

2. オブジェクトプールの理解

 

Pythonのインタニングのような最適化の挙動を理解するためにid()を使ってみることができます。

 

3. ハッシュ化の条件理解

 

dictのキーやsetの要素になれるオブジェクトは「ハッシュ可能(hashable)」である必要があります。ハッシュ可能であるための条件の一つが「不変であること」です。id()はその概念的な理解を助けます。

 

id()関数の注意点

 

  • 具体的な数値は重要ではない: id()が返す具体的な数値自体には意味がありません。異なる実行や異なる環境では異なる値になります。重要なのは、複数のid()呼び出しで同じ値が返されるか、異なる値が返されるかという点です。

  • ガベージコレクション: オブジェクトが不要になると、ガベージコレクタによってメモリが解放されることがあります。解放されたメモリのアドレスが、後で別の新しいオブジェクトに再利用される可能性があるため、古いオブジェクトのIDが新しいオブジェクトに割り当てられることもあり得ます。しかし、生存期間中の一意性は保証されます。

 

まとめ

 

Pythonのid()関数は、オブジェクトのメモリ上のユニークな識別子を返す組み込み関数であり、is演算子と密接に関連しています。この関数を理解することで、Pythonのオブジェクトモデル、特にミュータブルなオブジェクトとイミュータブルなオブジェクトの挙動の違いを深く把握することができます。

  • id()関数は、オブジェクトの生存期間中に一意な識別値を返します。

  • id()の結果は通常、オブジェクトのメモリアドレスに相当します。

  • is演算子id()の結果を比較し、オブジェクトの同一性をチェックします。

  • ミュータブルなオブジェクト(リストなど)は、内容が変更されてもid()は変わりません。

  • イミュータブルなオブジェクト(整数、文字列など)は、内容を変更する操作を行うと、新しいオブジェクトが作成されid()が変わります。

  • デバッグ時やPythonのオブジェクトモデルの理解を深めるのに役立ちます。

この関数を理解し適切に活用することで、Pythonコードの挙動をより正確に把握し、バグの特定やより効率的なコード設計に繋がるでしょう。


 

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座