2010-11-17 5 views
9

En python, on peut dire:quelles sont les méthodes utilisées par `foo <bar <baz`?

if foo < bar < baz: 
    do something. 

et de même, nous pouvons surcharger les opérateurs de comparaison comme:

class Bar: 
    def __lt__(self, other): 
     do something else 

mais les méthodes des types des opérandes de ces comparisions d'intervalle sont effectivement appelés? est l'équivalent ci-dessus à

if foo.__lt__(bar) and bar.__lt__(baz): 
    do something. 

Éditer: re S.Lott, Voici une sortie qui aide à illustrer ce qui se passe réellement.

>>> class Bar: 
    def __init__(self, name): 
     self.name = name 
     print('__init__', self.name) 
    def __lt__(self, other): 
     print('__lt__', self.name, other.name) 
     return self.name < other.name 

>>> Bar('a') < Bar('b') < Bar('c') 
('__init__', 'a') 
('__init__', 'b') 
('__lt__', 'a', 'b') 
('__init__', 'c') 
('__lt__', 'b', 'c') 
True 
>>> Bar('b') < Bar('a') < Bar('c') 
('__init__', 'b') 
('__init__', 'a') 
('__lt__', 'b', 'a') 
False 
>>> 
+0

+1: Vous pourriez (et fait) répondre à votre propre question. -1: Vous n'avez certainement pas besoin de poser cette question ici, car trouver la réponse était clairement plus facile et plus rapide que de demander. –

Répondre

4

Vous avez raison:

class Bar: 
    def __init__(self, name): 
     self.name = name 
    def __lt__(self, other): 
     print('__lt__', self.name, other.name) 
     return True 

a,b,c = Bar('a'), Bar('b'), Bar('c') 

a < b < c 

sortie :

('__lt__', 'a', 'b') 
('__lt__', 'b', 'c') 
True 
+0

dans une certaine mesure, je suis aussi très intéressé par savoir si «et» est utilisé, ou quelque autre chose intelligente se passe également. – SingleNegationElimination

+0

Avec quelques tests, on dirait qu'il doit faire quelque chose de fondamentalement similaire. Je peux faire en sorte que la seconde comparaison soit court-circuitée en faisant en sorte que la première soit fausse. – SingleNegationElimination

+0

@TokenMacGuy: Comme vous pouvez le voir dans mon deuxième exemple, c'est un peu plus qu'un simple et, puisque la barre pourrait être une fonction de mutation. –

12
if foo < bar < baz: 

est équivalent à

if foo < bar and bar < baz: 

avec une distinction importante: si la barre est un mutationniste, il sera mis en mémoire cache. À savoir:

if foo < bar() < baz: 

est équivalent à

tmp = bar() 
if foo < tmp and tmp < baz: 

Mais pour répondre à votre question, il finira par être:

if foo.__lt__(bar) and bar.__lt__(baz): 
+0

Wow, les trois premières lignes de mon message allaient être les mêmes que les vôtres, à l'exception d'un seul deux-points. +1 à vous. ;) –

1

Il appelle la méthode spéciale __lt__() et, au besoin, il appellera __nonzero__() pour forcer le résultat de __lt__() à un booléen. Étonnamment (pour moi au moins), il n'y a pas de méthode __and__() pour remplacer l'opérateur and.

Voici un programme de test:

#!/usr/bin/env python 

class Bar: 
    def __init__(self, value): 
     self.value = value 

    def __lt__(self, other): 
     print "%s.__lt__(%s)" % (self, other) 
     return Bar("%s.__lt__(%s)" % (self, other)) 

    def __nonzero__(self): 
     print "%s.__nonzero__()" % (self) 
     return True 

    def __str__(self): 
     return self.value 

foo = Bar("foo") 
bar = Bar("bar") 
baz = Bar("baz") 

if foo < bar < baz: 
    pass 

Sortie:

foo.__lt__(bar) 
foo.__lt__(bar).__nonzero__() 
bar.__lt__(baz) 
bar.__lt__(baz).__nonzero__() 
+0

'and' n'est pas un opérateur dans ce contexte, c'est un mécanisme pour décrire le flux de contrôle (évidemment dans un autre contexte il y a un' 'and '' pour lequel il y a un opérateur correct et un override' __and__'). Gardez à l'esprit que 'et 'ne compare rien, il évalue simplement chaque membre de l'expression pour la vérité, et quand il en trouve un qui est faux, il s'éclipse. Demander à une méthode de surcharger 'and 'revient à demander à une méthode de surcharger' if'. –

3

Il utilise des appels successifs au moins que l'opérateur de comparaison:

>>> import dis 
>>> def foo(a,b,c): 
...  return a < b < c 
... 
>>> dis.dis(foo) 
    2   0 LOAD_FAST    0 (a) 
       3 LOAD_FAST    1 (b) 
       6 DUP_TOP    
       7 ROT_THREE   
       8 COMPARE_OP    0 (<) 
      11 JUMP_IF_FALSE   8 (to 22) 
      14 POP_TOP    
      15 LOAD_FAST    2 (c) 
      18 COMPARE_OP    0 (<) 
      21 RETURN_VALUE   
     >> 22 ROT_TWO    
      23 POP_TOP    
      24 RETURN_VALUE   
+0

+1 pour le code généré. Cela ne fait que renforcer que la syntaxe tranchante que j'avais prévue pour une classe ne se produira pas en toute sécurité. Mais j'ai tellement appris que ça valait la peine de regarder. – SingleNegationElimination

Questions connexes