概要(id は「そのオブジェクトだけに割り当てられた識別値」を返す)
id は、渡したオブジェクトの“同一性”を表す識別値(identity)を返す組み込み関数です。これにより「2つの変数が同じオブジェクトを指しているのか」「コピーしたつもりが参照共有になっていないか」を、瞬時に見分けられます。表示は整数ですが、意味は「その実体を区別するためのラベル」です。
print(id(10)) # 例:整数の識別値(identity)
print(id("hello")) # 例:文字列の識別値
print(id([1, 2, 3])) # 例:リストの識別値
Python基本動作と「同一性」の考え方(ここが重要)
代入は“同じものへの別名”、id は同じ値を返す
変数へ代入すると、同じオブジェクトを指す“別名”ができます。id を比べると一致し、「同じ実体」であることが分かります。
a = [1, 2]
b = a
print(id(a), id(b)) # 同じ値
b.append(3)
print(a) # [1, 2, 3](同じオブジェクトに変更が反映)
Pythonコピーすると“別の実体”、id は異なる
浅いコピーでも深いコピーでも、新しいコンテナ(外側)が作られます。id は異なり、「別物」であることが確認できます(ただし浅いコピーは内側要素の参照を共有します)。
import copy
a = [[1], [2]]
b = a.copy() # 浅いコピー
c = copy.deepcopy(a) # 深いコピー
print(id(a), id(b), id(c)) # a と b/c は異なる
print(id(a[0]), id(b[0])) # 浅いコピーは内側参照を共有(同じ)
print(id(a[0]), id(c[0])) # 深いコピーは内側も別物(異なる)
Python同一性・等価性・型判定の線引き(id / is / == / isinstance を深掘り)
id と is は“同一性”の確認、== は“値の等価性”
- id(x) と id(y) が同じ、または x is y が True → 同じ実体(同一オブジェクト)
- x == y が True → 値として等しい(別オブジェクトでもあり得る)
x = [1, 2]
y = [1, 2]
print(x == y) # True(値が等しい)
print(x is y) # False(別オブジェクト)
print(id(x), id(y)) # 異なる
Pythonisinstance は「その型として扱えるか」の判定
isinstance(x, 型または型のタプル) は、継承を含めた「型ガード」。同一性ではなく、“扱い方”のための判定です。用途が違うので、混同しないこと。
class A: pass
class B(A): pass
b = B()
print(isinstance(b, A)) # True(A系として扱える)
Pythonよくある挙動と注意点(イミュータブル・ミュータブル・最適化)
イミュータブルは“再利用されることがある”
整数や短い文字列などの不変オブジェクトは、実装によって同じ値を再利用することがあります。そのため、同じ値を別変数に代入しても id が一致するケースを観測できます。これは“仕様依存の最適化”であり、常にそうなると期待してロジックを組むべきではありません。
a = 10
b = 10
print(id(a), id(b)) # 一致することがある(最適化)
s1 = "hello"
s2 = "hello"
print(id(s1), id(s2)) # こちらも一致することがある
Pythonミュータブルは“変更すれば実体は同じまま中身が変わる”
リスト・辞書・集合などは、変更してもオブジェクト自体は同じ(id 不変)です。参照共有のまま中身だけ変わるため、意図せぬ共有がないかを id で確認すると安心です。
items = [1, 2]
print(id(items))
items.append(3)
print(id(items)) # 変わらない(同じオブジェクトの中身が変化)
Pythonデバッグと設計での実用(参照共有の検出・防御的コーディング)
「共有されてしまった?」を即座に見抜く
関数に渡した引数を中で変更したら呼び出し元に影響するのか——ミュータブル引数の扱いはバグの温床です。id をログに出すだけで、共有かコピーかが明快になります。
def add_tag_inplace(post, tag):
print("post id:", id(post))
post.setdefault("tags", []).append(tag)
p = {"title": "coffee"}
add_tag_inplace(p, "drink")
add_tag_inplace(p, "hot")
print(p) # {'title': 'coffee', 'tags': ['drink', 'hot']}
Python“新しいものを返す”設計で副作用を避ける
中身を変えず新しいコンテナを返すと、id が変わり副作用を避けられます。テスト容易性も上がります。
def add_user(users, name):
new_users = users + [name]
print("old id:", id(users), "new id:", id(new_users))
return new_users
u = ["taro"]
u2 = add_user(u, "hanako") # 別オブジェクトを返す
Python実践例(定番から一歩先まで)
例題1:同一性の確認と値の等価性の違い
a = [1, 2]
b = a
c = [1, 2]
print(a == c) # True(値は等しい)
print(a is c) # False(別物)
print(a is b) # True(同一)
Python例題2:浅いコピーと深いコピーの id を比べる
import copy
src = [{"x": 1}, {"x": 2}]
shallow = src.copy()
deep = copy.deepcopy(src)
print(id(src), id(shallow), id(deep)) # 外側は全部異なる
print(id(src[0]), id(shallow[0])) # 浅いコピーは内側共有(同じ)
print(id(src[0]), id(deep[0])) # 深いコピーは内側も別(異なる)
Python例題3:イミュータブルの“最適化っぽい”挙動を観測
a = 100
b = 100
print(id(a) == id(b)) # True になることがある(実装の最適化)
s1 = "tea"
s2 = "t" + "ea" # コンパイル時に最適化されることがある
print(id(s1) == id(s2)) # True になることがある
Python例題4:引数が“同一オブジェクト”かどうかで分岐
def maybe_inplace(dst, src):
if dst is src:
# 同一オブジェクトなら何もしない(無駄な処理を避ける)
return dst
# 別物なら安全にコピーして結合
return dst + src
x = [1]
print(maybe_inplace(x, x)) # 同一 → そのまま
print(maybe_inplace([1], [2])) # 別物 → 新規作成
Pythonまとめ
id は「同一性」を確認するための基礎ツールです。代入は同一(id 一致)、コピーは別物(id 不一致)。値の等価性(==)や“扱えるか”の型判定(isinstance)とは目的が違います。イミュータブルには実装依存の再利用(同じ id を観測)があり得るため、それを前提にロジックを組まないこと。ミュータブルの参照共有は id ログで早期発見、設計は“新しいものを返す”方針で副作用を最小化する——この感覚を体に入れると、デバッグも設計も一段クリアに進みます。
