2012-06-12 11 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

除此之外,這不是最好的散列函數(因爲你不是散列一個普通的字符串,其中一個字符串組件的熵低得多因爲它已知由數字組成)。對於一個微不足道但非常有效的修復方法,分別取對象的哈希值並將其與xor進行比較。要獲得更多魔法,請將它們添加爲由素數常量縮放。 –

+0

@KonradRudolph:你的評論中有一個隱含的假設 - 特別需要一個「好」的散列才能讓這個集合表現出色。 Python的'set'實現並非如此;請參閱[來自Python源代碼的此評論](http://hg.python.org/cpython/file/26e2ee402a0b/Objects/dictobject.c#l113)以獲取更多解釋。 –

回答

40

您還需要以與__hash__()兼容的方式定義__eq__() - 否則,相等將基於對象標識。

在Python 2上,建議您也定義__ne__以使!===一致。在Python 3中,缺省__ne__實現將委託給__eq__

+6

事實上,在檢查散列是否相等之後,'dict' /'set'還必須在散列衝突的情況下檢查實際的相等性。 –

+0

@ user2357112 Definig'__ne__'不需要製作一個可排序的對吧?定義它可能是個好主意,否則'!='將會有相當奇怪的語義,但是如果你只想在集合或字典中使用類型,你並不需要它。 –

+0

@SvenMarnach:從技術上講,集合和字典不會在任何地方使用'!=',但實際上依賴於它是一個討厭的錯誤的祕訣。即使集合不使用'!=',有人可能會。如果你願意的話,你可以重新填寫它,但我認爲答案應該明確提及'__ne__';這個問題的精神似乎更多地是「我該怎麼做*正確*」而不是「爲了得到這段代碼片段來產生預期的輸出所需要的最低限度」。 – user2357112

6

The Python documentation可能會有所幫助:

如果一個類沒有定義一個__cmp__()__eq__()方法,它也不應該定義__hash__()操作要麼;

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