2016-06-18 8 views
1

Ich verwende Python 3.5.1 auf 64-Bit-Windows. Mein Problem ist Python scheint __eq__ und __lt__ Operatoren auf einer benutzerdefinierten Klasse beim Sortieren zu ignorieren. Die Verwendung eines benutzerdefinierten Sortierschlüssels ist eine Problemumgehung, die nicht funktioniert beim Versuch, Tupel zu sortieren, die Instanzen dieser Klasse enthalten.Python 3 Sortierung ignoriert __lt__ und __eq__

Beispiel:

class Symbol: 
    def __init__(self, name, is_terminal = False): 
    self.name = name 
    self.is_terminal = is_terminal 
    def __eq__(self, other): 
    return (self.is_terminal, self.name) == (other.is_terminal, other.name) 
    def __lt__(self, other): 
    return (self.is_terminal, self.name) < (other.is_terminal, other.name) 

symbols = set() 
for s in "abcdef": 
    symbols.add(Symbol(s)) 

sorted_symbols = sorted(symbols) 
# sorted_symbols now contain the symbols in random order 

Mit dem functools.total_ordering Dekorateur nicht hilft.

Meine Frage ist, wie definiere ich die Bestellung für eine Benutzerklasse in Python 3?

+0

Sortierung verwendet definitiv diese Methoden. Wie haben Sie festgestellt, dass sie nicht verwendet werden? Und warum ist ein Sortierschlüssel kein Workaround? 'key = Lambda s: (s.is_terminal, s.name)' würde Ihnen das gleiche Ergebnis geben. –

+0

Was ist das erwartete Ergebnis? Was bekommst du stattdessen? –

+0

Ihre 'Symbol' Klasse ist sortierbar, aber nicht wie geschrieben waschbar. Wenn Sie "__eq__" definieren, müssen Sie auch "__hash__" definieren oder Sie können die Instanzen nicht als Wörterbuchschlüssel verwenden oder sie zu Sätzen hinzufügen. Wenn Sie 'Symbole' in eine Liste geändert haben (und' append' statt 'add' in der Schleife verwenden), würde Ihr Code gut funktionieren. – Blckknght

Antwort

4

Python tut nicht ignorieren __eq__ und __lt__, zumindest nicht, wenn Sie @functools.total_ordering tatsächlich verwendet:

>>> from functools import total_ordering 
>>> @total_ordering 
... class Symbol: 
...  def __init__(self, name, is_terminal=False): 
...   self.name = name 
...   self.is_terminal = is_terminal 
...  def __repr__(self): 
...   return 'Symbol({0.name!r}, is_terminal={0.is_terminal!r})'.format(self) 
...  def __hash__(self): 
...   return hash(self.name)^hash(self.is_terminal) 
...  def __eq__(self, other): 
...   print('{} __eq__ {}'.format(self, other)) 
...   return (self.is_terminal, self.name) == (other.is_terminal, other.name) 
...  def __lt__(self, other): 
...   print('{} __lt__ {}'.format(self, other)) 
...   return (self.is_terminal, self.name) < (other.is_terminal, other.name) 
... 
>>> symbols = set() 
>>> for s in "abcdef": 
...  symbols.add(Symbol(s)) 
... 
>>> sorted(symbols) 
Symbol('f', is_terminal=False) __lt__ Symbol('c', is_terminal=False) 
Symbol('a', is_terminal=False) __lt__ Symbol('f', is_terminal=False) 
Symbol('a', is_terminal=False) __lt__ Symbol('f', is_terminal=False) 
Symbol('a', is_terminal=False) __lt__ Symbol('c', is_terminal=False) 
Symbol('b', is_terminal=False) __lt__ Symbol('c', is_terminal=False) 
Symbol('b', is_terminal=False) __lt__ Symbol('a', is_terminal=False) 
Symbol('d', is_terminal=False) __lt__ Symbol('c', is_terminal=False) 
Symbol('d', is_terminal=False) __lt__ Symbol('f', is_terminal=False) 
Symbol('e', is_terminal=False) __lt__ Symbol('c', is_terminal=False) 
Symbol('e', is_terminal=False) __lt__ Symbol('f', is_terminal=False) 
Symbol('e', is_terminal=False) __lt__ Symbol('d', is_terminal=False) 
[Symbol('a', is_terminal=False), Symbol('b', is_terminal=False), Symbol('c', is_terminal=False), Symbol('d', is_terminal=False), Symbol('e', is_terminal=False), Symbol('f', is_terminal=False)] 

In der Tat, Sortierung funktioniert auch ohne @total_ordering weil die Timsort Implementierung nur __lt__ für diesen Satz verwendet.

Ein Sortierschlüssel ist auch eine Option, geben Sie einfach die (is_terminal, name) Tupel aus dem Schlüssel:

>>> sorted(symbols, key=lambda s: (s.is_terminal, s.name)) 
[Symbol('a', is_terminal=False), Symbol('b', is_terminal=False), Symbol('c', is_terminal=False), Symbol('d', is_terminal=False), Symbol('e', is_terminal=False), Symbol('f', is_terminal=False)] 

Beachten Sie, dass jetzt die __lt__ Methode nie genannt wird, weil die Sortierschlüssel stattdessen verwendet wird.