2012-06-12 15 views
34

は私のコードです:オブジェクトを適切にハッシュ可能にするにはどうすればいいですか?ここ

class Hero: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

    def __str__(self): 
     return self.name + str(self.age) 

    def __hash__(self): 
     print(hash(str(self))) 
     return hash(str(self)) 

heroes = set() 

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407 
print(len(heroes)) # gets 1 

heroes.add(Hero('Lara Miheenko', 17)) # gets hash -2822451113328084695 
print(len(heroes)) # gets 2 

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407 
print(len(heroes)) # gets 3! WHY? 

なぜこの出来事はありますか?
第1と第3のオブジェクトは同じ内容と同じハッシュを持っていますが、len()は約3つの固有のオブジェクトを示していますか?

+0

わからないですが、あなたはおそらく '__eq__'または' __cmp__'を必要として:http://docs.python.org/glossary.html#term-hashable – nhahtdh

+2

これは別のところ、これは最良のハッシュ関数ではありません(一般的な文字列をハッシングしていないため、文字列要素の1つがはるかに低いエントロピー数字で構成されていることが分かっているため)。簡単ではあるがかなり効果的な修正をするには、オブジェクトのハッシュ値を別々にとり、それらをxorします。より多くの魔法の場合は、素数定数でスケーリングしたものを追加します。 –

+0

@ KonradRudolph:あなたのコメントには暗黙の前提があります。具体的には、セットがうまくいくためには「良い」ハッシュが必要です。これは、Pythonの 'set'実装では当てはまりません。詳しくは、[Pythonのソースからのこのコメント](http://hg.python.org/cpython/file/26e2ee402a0b/Objects/dictobject.c#l113)を参照してください。 –

答えて

40

また、__eq__()を互換性のある方法で__hash__()と定義する必要があります。そうでない場合、等価性はオブジェクトの同一性に基づいています。

__ne__を定義して、!===と一致させることをお勧めします。 Python 3では、デフォルトの__ne__実装が__eq__に委譲されます。

+6

実際、ハッシュが等しいかどうかをチェックした後、 'dict' /' set'はハッシュ衝突の場合にも実際の等価性をチェックする必要があります。 –

+0

@ user2357112型をハッシュ可能にするために '__ne__'を使う必要はありませんか?それ以外の '!='はむしろ奇妙なセマンティクスを持っているので、それを定義することは良い考えです。しかし、あなたがしたいのは、セットや辞書の型を使うことだけです。本当にそれを必要としません。 –

+0

@SvenMarnach:技術的には、setとdictsはどこでも '!='を使用しませんが、実際それは厄介なバグのレシピです。セットが '!='を使わなくても、誰かがおそらくやるでしょう。あなたが望むなら、あなたはそれを言い換えることができますが、私は答えが間違いなく「__ne__」に言及すべきだと思います。質問の精神は、「このコードスニペットに期待される成果を生み出すために必要な最小限のもの」よりも、「どうすれば*正しいことをやるのか」のように思えます。 – user2357112

6

The Python documentationは役に立つかもしれません:

クラスは、それはどちらか__hash__()操作を定義するべきではありません__cmp__()__eq__()メソッドを定義していない場合は、ここで

8

全体のコードです:

class Hero: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

    def __str__(self): 
     return self.name + str(self.age) 

    def __hash__(self): 
     print(hash(str(self))) 
     return hash(str(self)) 

    def __eq__(self,other): 
     return self.name == other.name and self.age== other.age 



heroes = set() 
heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407 
print(len(heroes)) # gets 1 

heroes.add(Hero('Lara Miheenko', 17)) # gets hash -2822451113328084695 
print(len(heroes)) # gets 2 

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407 
print(len(heroes)) # gets 2 

機能が__eq__を認識し、そのようなlenは2

関連する問題