2016-07-18 6 views
6

Ich benutze set() und __hash__ Methode der python Klasse zu verhindern, dass das gleiche Hash-Objekt im Satz hinzufügen. Vergleichen Sie gemäß python data-model document, set() dasselbe Hash-Objekt als dasselbe Objekt und fügen Sie sie einfach einmal hinzu.Python - Klasse __hash__ Methode und set

Aber es verhält sich anders als unten:

class MyClass(object): 

    def __hash__(self): 
     return 0 

result = set() 
result.add(MyClass()) 
result.add(MyClass()) 

print(len(result)) # len = 2 

Während bei String-Wert, funktioniert es richtig.

Meine Frage ist: Warum sind die gleichen Hash-Objekte nicht in Set identisch?

Antwort

10

Ihre Lesung ist falsch. Die __eq__-Methode wird für Gleichheitsprüfungen verwendet. Die Dokumente geben nur an, dass der Wert __hash__ für zwei Objekte a und b, für die a == b (d. H. a.__eq__(b)) gilt, gleich sein muss.

Dies ist ein gemeinsamer Logikfehler: a == b ist True bedeutet, dass hash(a) == hash(b) auch True ist. Die Implikation bedeutet jedoch nicht notwendigerweise Äquivalenz, die hash(a) == hash(b) würde bedeuten, dass a == b.

Um alle Instanzen von MyClass vergleichbar zu machen, müssen Sie eine __eq__ Methode für sie bereitstellen; andernfalls vergleicht Python ihre Identitäten stattdessen.Dies könnte tun:

class MyClass(object): 
    def __hash__(self): 
     return 0 
    def __eq__(self, other): 
     # another object is equal to self, iff 
     # it is an instance of MyClass 
     return isinstance(other, MyClass) 

Jetzt:

>>> result = set() 
>>> result.add(MyClass()) 
>>> result.add(MyClass()) 
1 

In Wirklichkeit werden Sie die __hash__ auf jenen Eigenschaften des Objekts stützen, die für __eq__ Vergleich verwendet werden, zum Beispiel:

class Person 
    def __init__(self, name, ssn): 
     self.name = name 
     self.ssn = ssn 

    def __eq__(self, other): 
     return isinstance(other, Person) and self.ssn == other.ssn 

    def __hash__(self): 
     # use the hashcode of self.ssn since that is used 
     # for equality checks as well 
     return hash(self.ssn) 

p = Person('Foo Bar', 123456789) 
q = Person('Fake Name', 123456789) 
print(len({p, q}) # 1 
5

Sets benötigen zwei Methoden ein Objekt hashable zu machen: __hash__ und __eq__. Zwei Instanzen müssen den gleichen Hash-Wert zurückgeben, wenn sie als gleich angesehen werden. Eine Instanz wird in einer Gruppe bereits als vorhanden betrachtet, wenn sowohl der Hash in der Gruppe als auch vorhanden ist. Die Instanz wird als einer der Instanzen mit demselben Hash in der Gruppe betrachtet.

Ihre Klasse implementiert __eq__ nicht, daher wird stattdessen der Standardwert object.__eq__ verwendet, der nur wahr zurückgibt, wenn obj1 is obj2 ebenfalls wahr ist. Mit anderen Worten, zwei Instanzen werden nur dann als gleich betrachtet, wenn sie die exakt gleiche Instanz sind.

Nur weil ihre Hashes übereinstimmen, macht sie sie nicht einzigartig, was ein Set betrifft; Selbst Objekte mit unterschiedlichen Hashwerten können in demselben Hash-Tabellen-Slot enden, da der Modulo des Hashs gegen die Tabellengröße verwendet wird.

hinzufügen eine benutzerdefinierte __eq__ Methode, die True zurückgibt, wenn zwei Instanzen sollen gleich sein:

def __eq__(self, other): 
    if not isinstance(other, type(self)): 
     return False 
    # all instances of this class are considered equal to one another 
    return True