2009-10-22 8 views
49

私のプログラムのPython 3.1フォークに機能を移植する際に、私は奇妙なバグがありました。`__eq__`を定義する型はハッシュできませんか?

Python 3.xでは、オブジェクトに__eq__メソッドがある場合、それは自動的に解けません。これは、次の仮説に絞りました。

これは本当ですか?

は、ここではPython 3.1で何が起こるかです:

>>> class O(object): 
...  def __eq__(self, other): 
...   return 'whatever' 
... 
>>> o = O() 
>>> d = {o: 0} 
Traceback (most recent call last): 
    File "<pyshell#16>", line 1, in <module> 
    d = {o: 0} 
TypeError: unhashable type: 'O' 

フォローアップの質問は、どのように私は私の個人的な問題を解決するのですか?私はいくつかのオブジェクトを指しているWeakKeyDictionaryを格納しているオブジェクトChangeTrackerを持っていて、過去の特定の時点でそれぞれのpickleダンプの値を与えています。既存のオブジェクトがチェックインされるたびに、変更トラッカーは新しいピクルが古いピクルと同じであるかどうかを示し、その間にオブジェクトが変更されたかどうかを示します。問題は、オブジェクトがライブラリ内にあるかどうかをチェックすることもできません。なぜなら、オブジェクトがハッシュ不能であるという例外を発生させるからです。 (原因は__eq__の方法です)これを回避するにはどうすればよいですか?

+3

あなたが '__hash__'メソッドを提供するとどうなりますか? – ndim

答えて

50

はい、__eq__を定義すると、デフォルトの__hash__(つまり、メモリ内のオブジェクトのアドレスをハッシュする)がなくなります。ハッシュは平等と一致する必要があるため、これは重要です。等しいオブジェクトは同じオブジェクトをハッシュする必要があります。

解決策は簡単です:__hash__と定義し、__eq__と定義してください。

+0

私は後世のために追加しましょう:私は '__hash__'を' return id(self) 'と定義しました。 –

+25

@ cool-RR:オブジェクトIDで比較するために '__eq __()'を定義しない限り(間違っていると思われます)、 '__hash __()'で 'id(self)'を使うのは間違っています。本当の '__eq __()'実装であなたの質問を更新すると、それを補完する '__hash __()'を提案できますか? –

+0

私もこの問題を解決しようとしてきました。私はこれを思いついた: 'def __eq __():return self .__ dict__ == other .__ dict__'と 'def __hash __():return hash(self .__ dict __。values)' –

1

私はpythonの専門家ではありませんが、eqメソッドを定義するときには、ハッシュメソッドも定義する必要があります(それはオブジェクトのハッシュ値を計算します)。そうでなければハッシング・メカニズムは、同じオブジェクトにヒットしたかどうか、または同じハッシュ値を持つ別のオブジェクトにヒットしたかどうかはわかりません。実際には、逆の場合、__eq__メソッドで等しいと見なされるオブジェクトの異なるハッシュ値が計算される可能性があります。

おそらく、そのハッシュ関数が何と呼ばれているか分かりませんが、__hash__でしょうか? :)

+6

FYI:私はこれをいつか前に描きました(http://www.mindmeister.com/10510492/python-underscore):すべてのアンダースコアメソッドのマインドマップをPythonで作成しました。 – jldupont

+2

実際には逆です。 2つのオブジェクトが同じハッシュ値を持っていても等しくない場合、それは問題ありません。 「ハッシングメカニズム」(すなわち、辞書)は最初にハッシュをチェックし、同じハッシュでも同等かどうかを比較する。実際の問題は、もう一方の方法で発生します。つまり、オブジェクトは別の方法でハッシュされますが、同じものを比較します。辞書はそれらを見つけるべきですが、そうではありません(またはあなたは辞書の重複したキーを得るかもしれません)。 –

+0

@Martin v。Löwis:私はそれを認識し、最後にそれを加えましたか、または私が言ったことと微妙な違いがありますか? – falstro

3

object.__hash__上のPython 3の手動チェック:このクラスは、それが__hash__()操作のいずれかを定義するべきではありません__eq__()メソッドが定義されていない場合

を。 __eq__()が定義されていて、__hash__()でない場合、そのインスタンスはハッシュ可能なコレクションのアイテムとして使用できません。

強調は私です。

あなたが怠惰になりたい場合は、あなただけのid(self)を返すように__hash__(self)を定義することができるように、それが聞こえる:

ユーザー定義クラスは、デフォルトでは__eq__()__hash__()メソッドを持っています。すべてのオブジェクトが(自分自身を除いて)等しくないものを比較し、x.__hash__()id(x)を返します。

+3

equals演算子をオーバーロードする唯一の理由は、2つの異なるオブジェクトが時にはequalを比較することができるからです(そうでなければ、それをオーバーロードする必要はありません)。この場合、 'return id(self)'ハッシュ関数は壊れています(等しいオブジェクトは同じものをハッシュしなければなりません、Martinの答えを見てください)。 "私は気にしない"ハッシュ関数は 'return 1'です。これは単純であり、すべての条件を満たす(非常に非効率的です!) –

17

親クラスから __hash__()の実装を保持する__eq__() ニーズをオーバーライドするクラスは、インタプリタが明示的__hash__ = <ParentClass>.__hash__を設定することにより、この を告げなければならない場合

http://docs.python.org/3.1/reference/datamodel.html#object.hashからこの段落。それ以外の場合、__hash__が明示的になしに設定されている場合と同様に、 の継承がブロックされ、 がブロックされます。

関連する問題