2013-11-01 13 views
5

Wpadłem w mylące zachowanie magicznych metod porównywania. Załóżmy, że mamy następujące klasy:Python magic method confusion

class MutNum(object): 
    def __init__ (self, val): 
     self.val = val 

    def setVal(self, newval): 
     self.val = newval 

    def __str__(self): 
     return str(self.val) 

    def __repr__(self): 
     return str(self.val) 

    # methods for comparison with a regular int or float: 
    def __eq__(self, other): 
     return self.val == other 

    def __gt__(self, other): 
     return self.val > other 

    def __lt__(self, other): 
     return self.val < other 

    def __ge__(self, other): 
     return self.__gt__(other) or self.__eq__(other) 

    def __le__(self, other): 
     return self.__lt__(other) or self.__eq__(other) 

Klasa robi to, co ma robić, porównując obiekt MutNum do regularnego int lub pływaka nie jest problemem. Jednak to jest to, czego nie rozumiem, nawet porównuje dobrze, gdy magiczne metody otrzymują dwa obiekty MutNum.

a = MutNum(42) 
b = MutNum(3) 
print(a > b) # True 
print(a >= b) # True 
print(a < b) # False 
print(a <= b) # False 
print(a == b) # False 

Dlaczego to działa? Dzięki.

+1

To może pomóc myśleć o '__gt__' i' __lt__' stojących w tej samej relacji co '__add__' i' __radd__', na przykład. Jeśli pierwszy nie ma zastosowania, Python próbuje drugi z odwróconymi operandami. – chepner

Odpowiedz

4

Ocenia się następująco (przy użyciu repr -jak notacji zamiast odnoszenia się do zmiennych):

MutNum(42) > MutNum(3) 
=> MutNum(42).__gt__(MutNum(3)) 
=> MutNum(42).val > MutNum(3) 
=> 42 > MutNum(3) 

a stamtąd to tylko int-MutNum Porównanie wiesz już prace.

+0

@ dust * You * powiedział, że porównywanie zwykłych int i floats z instancjami tej klasy nie stanowi problemu ;-) '42> ...' nie wywoła 'MutNum .__ gt__' ponownie, ponieważ 42 nie jest' MutNum'. Ten rodzaj zamieszania jest jednym z powodów, dla których nie definiujemy '__repr__', ale dodajemy coś, co odróżnia' MutNum's od liczby, którą otacza. – delnan

2

Jeśli wrzucisz niektóre wydruki i/lub sys.stderr.write, myślę, że zobaczysz, co się dzieje. EG:

def __gt__(self, other): 
    sys.stderr.write('__gt__\n') 
    sys.stderr.write('{}\n'.format(type(other))) 
    sys.stderr.write('{} {}\n'.format(self.val, other)) 
    result = self.val > other 
    sys.stderr.write('result {}\n'.format(result)) 
    return result 

def __lt__(self, other): 
    sys.stderr.write('__lt__\n') 
    sys.stderr.write('{}\n'.format(type(other))) 
    sys.stderr.write('{} {}\n'.format(self.val, other)) 
    result = self.val < other 
    sys.stderr.write('result {}\n'.format(result)) 
    return result 

Podczas próby porównać self.val (typu int) do innego (a MutNum), python zdaje sobie sprawę, że nie ma nic do porównywania int do MutNum i odwraca kolejność porównywania i porównuje MutNum do int - czyli coś, co zdefiniowałeś. Oznacza to, że pojedyncze> porównanie wykonuje> tak, jak można się spodziewać, ale także robi to <.