2019年11月10日

dict の key の注意点

Python で dict の key の重複判定は、以下のように行われます。
(hash(key1) == hash(key2)) and (key1 == key2)
ハッシュによる高速化を実現しつつ、万が一(兆が一?)のハッシュの衝突に備え、== 演算子も併用しています。

== 演算子が併用されることで、仮にハッシュの衝突が起こったとしても、問題とならない場合が多いです。

>>> hash('W')  # ある環境での文字列'W' のハッシュ値
332122003784942950

>>> hash(332122003784942950)  # 同じハッシュ値を持つ整数型
332122003784942950

# 'W' と 332122003784942950 を key とする dict でも
# == 演算子のおかげで別々の key となる
>>> d = {'W': None, 332122003784942950: None}
>>> d
{'W': None, 332122003784942950: None}



他方で、意外にあっさりと困るパターンも見つかります。int, float, bool, complex はハッシュ値の計算、および ==演算子に共通性があるためです。
>>> hash(1)  # 整数 1 のハッシュ値は 1
1
>>> hash(1.0)  # 浮動小数 1.0 のハッシュ値は 1
1
>>> hash(True)  # 論理型 True のハッシュ値は 1
1
>>> hash(1 + 0j)  # 複素数 1 + 0j のハッシュ値は 1
1

>>> 1 == 1.0 == True == 1 + 0j  # 1 と 1.0 と True と 1 + 0j は同値
True

# 1 と 1.0 と True と 1 + 0j を key とする dict を作成すると、
# 重複 key と判定され、1つの key になる
>>> d = {1: None, 1.0: None, True: None, 1 + 0j: None}
>>> d
{1: None}

結論として、dict の key にあまり色々な型を混ぜるのは得策ではありません。可能なら、文字列型と整数型(およびそれを tuple で組み合わせたもの)だけにするのが良いでしょう。

0 件のコメント:

コメントを投稿