2012-10-19 10 views
5

Ich versuche set().issubset() zum Vergleich der Sequenzen zu verwenden. Wie Sie sich vorstellen können, funktioniert es nicht wie erwartet;) Im Voraus: Entschuldigung für den langen Code-Blob.Python set(). Issubset() funktioniert nicht wie erwartet

class T(object): 
    def __init__(self, value, attributes = None): 
    self.value = value 
    self.attributes = Attributes(attributes) 

    def __eq__(self, other): 
    if not isinstance(other, T): 
     return False 
    if self.value == other.value and self.attributes == other.attributes: 
     return True 
    else: 
     return False 

    def __ne__(self, other): 
    if not isinstance(other, T): 
     return True 
    if self.value != other.value or self.attributes != other.attributes: 
     return True 
    else: 
     return False 

class Attributes(dict): 
    def __init__(self, attributes): 
    super(dict, self) 
    self.update(attributes or dict()) 

    def __eq__(self, other): 
    if self.items() == other.items(): 
     return True 
    else: 
     return False 

    def __ne__(self, other): 
    if not self.items() == other.items(): 
     return True 
    else: 
     return False 

    def __cmp__(self, other): 
    return self.items().__cmp__(other.items()) 


x = [T("I", {'pos': 0}), T("am", {'pos': 1}), T("a", {'pos': 2}), T("test", {'pos': 3})] 
y = [T("a", {'pos': 2}), T("test", {'pos': 3})] 
xx = set(x) 
yy = set(y) 

assert y[0] == x[2], "__eq__ did fail, really?" #works 
assert y[1] == x[3], "__eq__ did fail, really?" #works 
assert xx-(yy-xx) == xx, "set subtract not working" #works, but is nonsense. see accepted answer 
assert not xx.issubset(yy), "i am doing it wrong..." #works 
assert yy.issubset(xx), "issubset not working :(" #FAILS! 

oben Code schlägt fehl, die letzte Behauptung:

$ python 
Python 2.7.2 (v2.7.2:8527427914a2, Jun 11 2011, 15:22:34) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import issubsettest 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "issubsettest.py", line 52, in <module> 
    assert yy.issubset(xx), "issubset not working :(" 
AssertionError: issubset not working :(
>>> 

Was ich hier fehlt?

+0

Ich denke, dass dies, weil Benutzer –

+0

Ich denke, das Problem ist, dass Ihre Klasse 'T' überschreibt die Methode' __hash__'. Siehe [Dokumentation] (http://docs.python.org/reference/datamodel.html#object.__hash__): * Wenn es __cmp __() oder __eq __() aber nicht __hash __() definiert, sind seine Instanzen nicht verwendbar in Hash-Sammlungen.* – halex

+2

Ich habe gerade Ihren Code mit Python 3.3 getestet, was den Fehler gibt: 'TypeError: nicht hashbarer Typ: 'T'' in der Zeile' xx = set (x) 'also ist es definitiv das Fehlen von' __hash__'. – halex

Antwort

9

Ihre Objekte werden durch ihre id Hash-Wert (Sie haben nicht überschrieben __hash__). Natürlich sind sie keine Untermengen, da xx und yy eindeutige Objekte enthalten.

Um dies zu tun, müssen Sie mit einer Art von __hash__ Funktion kommen. __hash__ sollte immer den gleichen Wert für ein Objekt zurückgeben, weshalb es normalerweise so verstanden wird, dass Sie ein hashbares Objekt nicht mutieren.

class T(object): 
    #<snip> ... 

    def __hash__(self): 
     return hash(self.value) 

    #... </snip> 

mit dem Verständnis, dass self.value nicht für das Leben des Objekts ändern kann: Zum Beispiel könnte eine Wahl sein. (Hinweis, ich behaupte nicht, dass eine gute Wahl ist die tatsächliche Hash Sie verwenden wirklich abhängig ist auf der tatsächlichen Anwendung.)


Nun ist die, warum - Die Magie hinter Sätze (und dicts) und Die erstaunliche Leistung ist, dass sie sich auf Hashs verlassen. Grundsätzlich wird jedes hashbare Objekt in eine "einzigartige" (in einer perfekten Welt) Nummer umgewandelt. Python nimmt diese "eindeutige" Zahl und verwandelt sie in einen Array-Index, den sie verwenden kann, um das Objekt zu handhaben (die Magie ist hier ein wenig schwierig zu erklären, aber für diese Diskussion ist es nicht wichtig). Anstatt nach einem Objekt zu suchen, indem man es mit allen anderen Objekten im "Array" vergleicht (normalerweise eine Tabelle genannt) - eine teure Operation, weiß es genau, wo es nach dem Objekt suchen muss (basierend auf dem Hash-Wert). . Standardmäßig werden benutzerdefinierte Objekte durch ihre id (Speicheradresse) gehashed. Wenn Sie die Menge xx erstellen, betrachtet Python jedes der Objekte id s und fügt sie basierend auf ihren IDs ein. Nun, wenn Sie xx.issubset(yy) tun, sieht Python alle IDs in xx und überprüft, ob sie alle in yy sind. Aber keiner von ihnen sind in yy da sie alle eindeutige Objekte sind (und daher eindeutige Hash-Werte haben).

Aber Sie sagen, "warum hat xx-(yy-xx) == xx funktioniert?" Gute Frage. Lasst uns das auseinander ziehen.

Zuerst haben wir (yy - xx). Dies gibt einen leeren Satz zurück, denn auch in xx sind keine Elemente in yy (sie alle Hash zu unterschiedlichen Werten, da sie alle eindeutige id s haben). So machst du wirklich

xx - set([]) == xx 

das ist ziemlich offensichtlich sein sollte, warum das True ist.

+0

dann erklären, wie er es beheben sollte. – JBernardo

+0

@JBernardo - reparieren es ist eine andere Geschichte alle zusammen. OP muss irgendwie "__hash__" überschreiben. Aber die Wahl eines passenden '__hash__' hängt wirklich davon ab, wofür es verwendet wird. – mgilson

+0

Ja, sag ihm das. – JBernardo

Verwandte Themen