2016-06-18 3 views
1

64ビットウィンドウでPython 3.5.1を使用しています。私の問題は、ソート時にPythonがユーザ定義クラスの__eq____lt__演算子を無視するように見えることです。 カスタム並べ替えキーを使用すると、このクラスのインスタンスを含むタプルを並べ替えるときにが動作しない回避策です。Python 3の並べ替えで__lt__と__eq__が無視される

例:を助けないfunctools.total_orderingデコレータを使用して

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 

私の質問は、Python 3でユーザークラスの順序を定義する方法です。

+0

ほとんどの場合、ソートでこれらのメソッドが使用されます。それらが使われていないとあなたはどのように判断しましたか?ソートキーはなぜ回避策ではないのですか? 'key = lambda s:(s.is_terminal、s.name)'は同じ結果を返します。 –

+0

期待される結果は何ですか?代わりに何を得ますか? –

+0

'Symbol'クラスはソート可能ですが、記述されたようにハッシュ可能ではありません。 '__eq__'を定義する場合、' __hash__'を定義する必要があります。または、インスタンスを辞書キーとして使用したり、それらをセットに追加することはできません。 'symbols'をリストに変更した場合(ループで' add'ではなく 'append'を使用します)、あなたのコードはうまく動作します。 – Blckknght

答えて

4

Pythonはない__eq__を無視し__lt__んが、実際に@functools.total_orderingを使用した場合、少なくともではない:

>>> 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)] 

実際にTimSort実装が唯一のこのセットのために__lt__を使用しているため、でもソートは@total_orderingせずに動作します。ソートキーが代わりに使用されているので、今__lt__メソッドが呼び出されることはありません

>>> 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)] 

注:

ソートキーは、ちょうど鍵から(is_terminal, name)タプルを返し、またオプションです。

関連する問題