web-dev-qa-db-ja.com

`__eq__`を定義する型はハッシュできませんか?

プログラムのPython 3.1フォークに機能を移植するときに、奇妙なバグがありました。次の仮説に絞り込みました。

Python 2.xとは対照的に、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'

フォローアップの質問は、どうすれば個人的な問題を解決できるかということです。複数のオブジェクトを指すChangeTrackerを格納するオブジェクトWeakKeyDictionaryがあり、過去の特定の時点でのピクルスダンプの値をそれぞれに与えています。既存のオブジェクトがチェックインされるたびに、変更トラッカーは、新しいピクルスが古いピクルスと同一であるかどうかを示します。したがって、その間にオブジェクトが変更されたかどうかを示します。問題は、指定されたオブジェクトがライブラリにあるかどうかを確認することすらできないことです。これにより、オブジェクトがハッシュできないという例外が発生するためです。 (__eq__メソッドがあるためです。)これを回避するにはどうすればよいですか?

63
Ram Rachum

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

解決策は簡単です。__hash__を定義するとともに__eq__を定義するだけです。

63

http://docs.python.org/3.1/reference/datamodel.html#object。hash からのこの段落

__eq__()をオーバーライドするクラスが親クラスからの__hash__()の実装を保持する必要がある場合、インタプリタは___hash__ = <ParentClass>.__hash___を設定することによってこれを明示的に通知する必要があります。そうしないと、___hash___が明示的にNoneに設定されているかのように、__hash__()の継承がブロックされます。

22
newacct

Python 3マニュアル _object.__hash___

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

強調は私のものです。

怠惰になりたい場合は、__hash__(self)を定義してid(self)を返すことができるようです。

ユーザー定義クラスには、デフォルトで__eq__()メソッドと__hash__()メソッドがあります。それらを使用すると、すべてのオブジェクトが等しくなく(自分自身を除く)比較され、x.__hash__()id(x)を返します。

3
Mark Rushakoff

私はpythonエキスパートではありませんが、eq-methodを定義するときに、ハッシュメソッド(ハッシュ値を計算する)も定義する必要があることは意味がありません。オブジェクトの場合)そうでない場合、ハッシュメカニズムは、同じオブジェクトにヒットしたのか、同じハッシュ値を持つ別のオブジェクトにヒットしたのかを認識しません。実際には、その逆で、おそらく異なるハッシュ値を計算することになります。 __eq__メソッドによって等しいと見なされたオブジェクトの場合。

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

1
falstro