Python 2.x ma dwa sposoby na przeciążenie operatorów porównania __cmp__
lub „bogatych operatorów porównania”, takich jak __lt__
. Mówi się, że preferowane są bogate przeciążenia porównawcze, ale dlaczego tak jest?
Bogate operatory porównania są prostsze w implementacji każdego z nich, ale musisz zaimplementować kilka z nich z prawie identyczną logiką. Jeśli jednak możesz użyć cmp
porządkowania wbudowanego i krotki, to __cmp__
robi się całkiem proste i spełnia wszystkie porównania:
class A(object):
def __init__(self, name, age, other):
self.name = name
self.age = age
self.other = other
def __cmp__(self, other):
assert isinstance(other, A) # assumption for this example
return cmp((self.name, self.age, self.other),
(other.name, other.age, other.other))
Ta prostota wydaje się znacznie lepiej spełniać moje potrzeby niż przeładowanie wszystkich 6 (!) Bogatych porównań. (Jednak możesz sprowadzić to do „tylko” 4, jeśli polegasz na „zamienionym argumencie” / odzwierciedlonym zachowaniu, ale moim skromnym zdaniem powoduje to wzrost netto komplikacji).
Czy są jakieś nieprzewidziane pułapki, o których muszę wiedzieć, jeśli tylko przeciążę __cmp__
?
Rozumiem <
, <=
, ==
itp operatorzy mogą być przeciążone dla innych celów, a każdy obiekt może powrócić im się podoba. Nie pytam o zalety tego podejścia, a tylko o różnice, gdy używam tych operatorów do porównań w tym samym sensie, co dla liczb.
Aktualizacja: Jak zauważył Christopher , cmp
znika w 3.x. Czy są jakieś alternatywy, które sprawiają, że wykonywanie porównań jest tak proste, jak powyższe __cmp__
?
źródło
Odpowiedzi:
Tak, łatwo jest zaimplementować wszystko, np.
__lt__
Z klasą mixin (lub metaklasą, lub dekoratorem klas, jeśli tak się dzieje).Na przykład:
Teraz twoja klasa może zdefiniować po prostu
__lt__
i pomnożyć dziedziczenie z ComparableMixin (po wszelkich innych podstawach, których potrzebuje). Dekorator klas byłby dość podobny, po prostu wstawiałby podobne funkcje jako atrybuty nowej klasy, którą dekoruje (wynik może być mikroskopijnie szybszy w czasie wykonywania, przy równie niewielkim koszcie pamięci).Oczywiście, jeśli twoja klasa ma jakiś szczególnie szybki sposób na zaimplementowanie (np.)
__eq__
I__ne__
powinna zdefiniować je bezpośrednio, aby wersje miksera nie były używane (na przykład, tak jestdict
) - w rzeczywistości__ne__
można to zdefiniować, aby ułatwić tak jak:ale w powyższym kodzie chciałem zachować przyjemną symetrię tylko używania
<
;-). Dlaczego__cmp__
musiałeś odejść, ponieważ nie mają__lt__
i przyjaciele, dlaczego zachować kolejny, inny sposób to zrobić dokładnie to samo dookoła? To po prostu tak dużo martwej wagi w każdym środowisku uruchomieniowym Pythona (Classic, Jython, IronPython, PyPy, ...). Kod na pewno nie będzie zawierał błędów, to kod, którego nie ma - skąd zasada Pythona, że powinien istnieć jeden oczywisty sposób wykonania zadania (C ma tę samą zasadę w sekcji „Spirit of C” w przy okazji standard ISO).To nie znaczy, wychodzimy z naszej drodze do zakazania rzeczy (np bliskiej równoważność wstawek i klasa dekoratorów dla niektórych zastosowań), ale na pewno nie oznacza, że nie lubią nosić kod w kompilatorów i / lub środowiska wykonawcze, które istnieją nadmiarowo tylko po to, aby obsługiwać wiele równoważnych podejść do wykonania dokładnie tego samego zadania.
Dalsza edycja: w rzeczywistości istnieje jeszcze lepszy sposób na zapewnienie porównania ORAZ haszowanie dla wielu klas, w tym w pytaniu -
__key__
metoda, jak wspomniałem w komentarzu do pytania. Ponieważ nigdy nie udało mi się napisać dla niego PEP, musisz obecnie zaimplementować go z Mixinem (& c), jeśli chcesz:Bardzo częstym przypadkiem jest, gdy porównania instancji z innymi instancjami sprowadzają się do porównania krotki dla każdego z kilkoma polami - a następnie haszowanie powinno być realizowane na dokładnie tej samej podstawie. Te
__key__
specjalne adresy metody, które muszą bezpośrednio.źródło
TypeError: Cannot create a consistent method resolution order (MRO) for bases object, ComparableMixin
kiedy spróbuję w Pythonie 3. Zobacz pełny kod na gist.github.com/2696496functools.total_ordering
zamiast tworzyć własneComparableMixim
. Jak zasugerowano w odpowiedzi jmagnusson<
do implementacji__eq__
w Pythonie 3 jest dość złym pomysłem z powoduTypeError: unorderable types
.Aby uprościć ten przypadek, istnieje dekorator klas w Pythonie 2.7 + / 3.2 +, functools.total_ordering , który może być użyty do zaimplementowania tego, co sugeruje Alex. Przykład z dokumentów:
źródło
total_ordering
jednak nie wdraża__ne__
, więc uważaj!__ne__
. ale to dlatego, że__ne__
ma domyślną implementację, która jest delegowana__eq__
. Więc nie ma tu na co uważać.Jest to objęte PEP 207 - Rich Comparisons
Ponadto
__cmp__
odchodzi w Pythonie 3.0. (Zauważ, że nie ma go na http://docs.python.org/3.0/reference/datamodel.html, ale jest na http://docs.python.org/2.7/reference/datamodel.html )źródło
(Edytowano 17.06.2017 w celu uwzględnienia komentarzy).
Wypróbowałem podobną odpowiedź mixin powyżej. Wpadłem w kłopoty z „Brak”. Oto zmodyfikowana wersja, która obsługuje porównania równości z wartością „Brak”. (Nie widziałem powodu, by zawracać sobie głowę porównaniami nierówności z None jako pozbawionym semantyki):
źródło
self
może być SingletonNone
ofNoneType
a jednocześnie realizować swojeComparableMixin
? I rzeczywiście, ten przepis jest zły dla Pythona 3.self
będzie nigdy byćNone
tak, że oddział może przejść w całości. Nie używajtype(other) == type(None)
; po prostu użyjother is None
. Zamiast specjalnego-obudowyNone
, sprawdzić, czy inny typ jest przykładem typuself
i zawrócićNotImplemented
pojedyncza jeśli nie:if not isinstance(other, type(self)): return NotImplemented
. Zrób to dla wszystkich metod. Python może wtedy dać drugiemu operandowi szansę na podanie odpowiedzi.Zainspirowany
ComparableMixin
iKeyedMixin
odpowiedziami Alexa Martellego wymyśliłem następujący mixin. Umożliwia zaimplementowanie pojedynczej_compare_to()
metody, która używa porównań opartych na kluczach podobnych doKeyedMixin
, ale umożliwia Twojej klasie wybranie najbardziej wydajnego klucza porównania na podstawie typuother
. (Zauważ, że ta mieszanka nie pomaga zbytnio w przypadku obiektów, które można przetestować pod kątem równości, ale nie porządku).źródło