2013-11-01 14 views
5

Mi sono imbattuto in un comportamento confuso dei metodi di confronto magico. Supponiamo di avere la seguente classe:Confusione di metodi magici in Python

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) 

La classe fa quello che deve fare, mettendo a confronto un oggetto MutNum ad un normale float int o non è un problema. Tuttavia, e questo è ciò che non capisco, è paragonabile anche quando i metodi magici ricevono due oggetti 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 

Perché funziona? Grazie.

+1

Potrebbe essere utile pensare a '__gt__' e' __lt__' nella stessa relazione di '__add__' e' __radd__', ad esempio. Se il primo non si applica, Python prova l'altro con gli operandi invertiti. – chepner

risposta

4

si valuta come segue (utilizzando una notazione repr -come invece di fare riferimento alle variabili):

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

E da lì, è solo l'int-MutNum confronto si conosce già le opere.

+0

@dust * Si * ha detto che il confronto tra int inte e float su istanze di questa classe non è un problema ;-) '42> ...' non invocherà 'MutNum .__ gt__' perché 42 non è un' MutNum'. Questo tipo di confusione è una ragione per non definire '__repr__' come hai ma aggiungere qualcosa che distinguihes' MutNum's dal numero che include. – delnan

2

Se si inseriscono alcune stampe e/o sys.stderr.write, penso che vedrete cosa sta succedendo. 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 

Quando si tenta di confrontare self.val (un int) ad altri (un MutNum), pitone si rende conto che non ha nulla per il confronto di un int a un MutNum, e inverte l'ordine del confronto, e confronta un MutNum con un int - che è qualcosa che hai definito. Cioè, un singolo> confronto sta facendo il> come ti aspetteresti, ma sta anche facendo un <.

Problemi correlati