ハッシュ値の計算方法 (2)

list の __hash__ が TypeError を投げるで間違いなさげ。

やはり挙動から推測した通り。しかし一般には、実装==仕様ではないが、公式サイトにある「Python Reference Manual」が仕様と考えて良いのかな。

以下は、「Pythonリファレンスマニュアル」からの抜粋。

__hash__( self)

辞書演算 の際にキーとなるオブジェクトに対して呼び出されたり、組み込み関数 hash() から呼び出されたりします。 辞書演算におけるハッシュ値として利用できる、32 ビットの整数を返さなければなりません。このメソッドに必要な性質は、比較結果が等価であるオブジェクトは同じハッシュ値をもつということです; オブジェクト間で比較を行う際には、オブジェクトの各要素に対するハッシュ値を (排他的論理和をとるなどして) 何らかの方法で混合するよう勧めます。クラスが __cmp__() メソッドを定義していない場合、 __hash__() メソッドも定義してはなりません; クラスが __cmp__() または __eq__() を定義しているが、 __hash__() を定義していない場合、インスタンスを辞書のキーとして使うことはできません。クラスが変更可能なオブジェクトを定義しており、__cmp__() または __eq__() メソッドを実装している場合、__hash__() を定義してはなりません。これは、辞書の実装においてハッシュ値が変更不能であることが要求されているからです (オブジェクトのハッシュ値が変化すると、キーが誤ったハッシュバケツ: hash bucket に入っていることになってしまいます)。

"クラスが変更可能なオブジェクトを定義しており、__cmp__() または __eq__() メソッドを実装している場合、__hash__() を定義してはなりません。"とあるが、新スタイルクラスでは不可能ではないのかな。旧スタイルクラスのことを言っているのかな?新スタイルクラスの場合は、自前の可変クラスを実装する場合、ビルトインと同様に__hash__でTypeErrorを投げるように実装すべきだと思う。


旧スタイルクラス、新スタイルクラスに関わらず以下の通り。

__hash__が定義されている? __cmp__、__eq__が定義されている? どうなる?
Yes No キーにできるが挙動がおかしくなる
No Yes TypeError
Yes Yes 問題ない
No No キーにできるが意味あるかは別


新スタイルクラスでは、恐らく__hash__が定義されていない状態が不可能。つまり上の表で、2通りに限られる。つまり新スタイルクラス限定で話をすれば、辞書のキーにしたいなら自前の__hash__を定義したら、__cmp__もしくは__eq__も定義しようという説明でOK。


ちなみに、『Pythonチュートリアル』の用語のディクショナリの説明は以下のように書かれていた。

キーはゼロから始まる整数に限らず、__hash__()関数を持つオブジェクトであればよい。

新スタイルクラス限定なら確かにそうだと思うけど、__hash__が正しい値を返すように定義されていればよいという説明の方が良いと思う。listも__hash__を持っているので。しかし、旧スタイルクラスや__cmp__、__eq__との関係もあるので、一言で説明するのは難しい気がする。