問題在於它沒有意義,散列用於執行對象的有效分區。因此,當你有一個散列表實現的集合時,每個散列指向一個存儲區,通常是一個元素列表。爲了檢查一個元素是否在集合(或其他基於散列的容器)中,您轉到由散列指向的桶,然後遍歷列表中的所有元素,逐個比較它們。
換句話說 - 哈希不應該是一個比較器(因爲它可以,並應該給你有時誤報)。特別是,在你的例子中,你的設置不起作用 - 它不會識別重複,因爲它們不會相互比較。
class Foo:
def __eq__(self,other):
return self.Value==other.Value
def __hash__(self):
return id(self.Name)
a = set()
el = Foo()
el.Name = 'x'
el.Value = 1
el2 = Foo()
el2.Name = 'x'
el2.Value = 2
a.add(el)
a.add(el2)
print len(a) # should be 1, right? Well it is 2
實際上它是更糟糕的則是,如果你有2個對象具有相同的值,但不同的名稱,他們不認爲是相同的或者
class Foo:
def __eq__(self,other):
return self.Value==other.Value
def __hash__(self):
return id(self.Name)
a = set()
el = Foo()
el.Name = 'x'
el.Value = 2
el2 = Foo()
el2.Name = 'a'
el2.Value = 2
a.add(el)
a.add(el2)
print len(a) # should be 1, right? Well it is 2 again
同時適當做(這樣「如果== b,則散列的(a)==散列(b)」)給出:
class Foo:
def __eq__(self,other):
return self.Name==other.Name
def __hash__(self):
return id(self.Name)
a = set()
el = Foo()
el.Name = 'x'
el.Value = 1
el2 = Foo()
el2.Name = 'x'
el2.Value = 2
a.add(el)
a.add(el2)
print len(a) # is really 1
更新
有一個也是一個非確定性的部分,這是很難輕易再現,但基本上哈希並不唯一定義一個桶。通常它就像
bucket_id = hash(object) % size_of_allocated_memory
因此,具有不同哈希的東西仍然可能在同一個桶中結束。因此,即使名稱是不同的,也可以通過其他方式取決於實際的內部實現,內存約束等,因此可以獲得與每個值(內部集合)相等的兩個元素。
一般有更多的例子,事情可以去錯了,因爲哈希是定義爲功能h : X -> Z
這樣x == y => h(x) == h(y)
,這樣的人實現自己的容器,授權協議和其他工具都是免費的,以承擔這屬性。如果你破壞它 - 每一個使用哈希的工具都可能中斷。此外,它可以在時間打破,這意味着你更新一些庫,你的代碼將停止工作,作爲一個有效的更新底層庫(使用上述的前提下),可能會導致利用你違反這個假設。
更新2
最後,爲了解決您的問題 - 你根本不應該定義你EQ,LT運營商來處理排序。它大約是元素的實際比較,它應該與其餘行爲兼容。所有你需要做的就是定義一個單獨的比較並在排序程序使用它(在Python分揀接受任何比較,你並不需要依靠<,>等)。另一種方法是改爲有效的<,>,=定義在值上,但爲了保持名稱的唯一性 - 保留一個集合,包括......好的名稱,而不是對象本身。無論你選擇哪條路徑 - 這裏的關鍵要素是: 等號和散列必須兼容,就是這樣。
如果是'Value'這是平等重要的是,你一般實行'__hash__'爲'返回散列(self.Value)'。 – jonrsharpe
在這種情況下,值不足以識別身份,僅適用於大小。通過值__eq_和_lt_以及其他值一起定義它是允許類可以按值排序的方式,但具有相同值的實例不會被視爲相同。 – Lukas
你可以給一個不太抽象的例子嗎?目前這個問題似乎是你的班級沒有被一致定義。 – jonrsharpe