Assoziativität von "in" in Python?

107

Ich mache einen Python-Parser, und das verwirrt mich wirklich :

>>>  1 in  []  in 'a'
False

>>> (1 in  []) in 'a'
TypeError: 'in <string>' requires string as left operand, not bool

>>>  1 in ([] in 'a')
TypeError: 'in <string>' requires string as left operand, not list

Wie genau funktioniert "in" in Python in Bezug auf Assoziativität usw.?

Warum verhalten sich keine zwei dieser Ausdrücke gleich?

user541686
quelle
6
Sie treffen wahrscheinlich auf das hier beschriebene Verhalten: docs.python.org/reference/expressions.html#not-in , mit dem Sie if a < b < c:intuitiv schreiben und arbeiten können
millimoose
3
@millimoose: Ja, ich habe einfach nie inals "Vergleichs" -Operator gedacht, denke ich. : \
user541686

Antworten:

123

1 in [] in 'a'wird bewertet als (1 in []) and ([] in 'a').

Da die erste Bedingung ( 1 in []) ist False, wird der gesamte Zustand bewertet , wie False; ([] in 'a')wird nie tatsächlich ausgewertet, daher wird kein Fehler ausgelöst.

Hier sind die Anweisungsdefinitionen:

In [121]: def func():
   .....:     return 1 in [] in 'a'
   .....: 

In [122]: dis.dis(func)
  2           0 LOAD_CONST               1 (1)
              3 BUILD_LIST               0
              6 DUP_TOP             
              7 ROT_THREE           
              8 COMPARE_OP               6 (in)
             11 JUMP_IF_FALSE            8 (to 22)  #if first comparison is wrong 
                                                    #then jump to 22, 
             14 POP_TOP             
             15 LOAD_CONST               2 ('a')
             18 COMPARE_OP               6 (in)     #this is never executed, so no Error
             21 RETURN_VALUE         
        >>   22 ROT_TWO             
             23 POP_TOP             
             24 RETURN_VALUE        

In [150]: def func1():
   .....:     return (1 in  []) in 'a'
   .....: 

In [151]: dis.dis(func1)
  2           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               3 (())
              6 COMPARE_OP               6 (in)   # perform 1 in []
              9 LOAD_CONST               2 ('a')  # now load 'a'
             12 COMPARE_OP               6 (in)   # compare result of (1 in []) with 'a'
                                                  # throws Error coz (False in 'a') is
                                                  # TypeError
             15 RETURN_VALUE   



In [153]: def func2():
   .....:     return 1 in ([] in 'a')
   .....: 

In [154]: dis.dis(func2)
  2           0 LOAD_CONST               1 (1)
              3 BUILD_LIST               0
              6 LOAD_CONST               2 ('a') 
              9 COMPARE_OP               6 (in)  # perform ([] in 'a'), which is 
                                                 # Incorrect, so it throws TypeError
             12 COMPARE_OP               6 (in)  # if no Error then 
                                                 # compare 1 with the result of ([] in 'a')
             15 RETURN_VALUE        
Ashwini Chaudhary
quelle
Whoa !! +1 Das ist unglaublich, vielen Dank! Es sieht sehr praktisch aus, wenn ich nur davon wüsste! Wissen Sie zufällig, wo dies in der Dokumentation steht? Ich schaute , konnte aber nichts finden, was dies nahelegte!
user541686
1
Hinweis: []ist falsch, wird aber []nicht FalsezB [] and anythingzurückgegeben [](nicht False).
JFS
6
@Mehrdad Schauen Sie sich den Python-Disassembler an , der mit iPython verwendet wurde zum Generieren dieser Ausgabe verwendet wurde.
Jeff Ferland
Keine Ahnung, welche Version von Python dies generiert hat, aber Python 3.2 hat anscheinend einen neuen Bytecode: JUMP_IF_FALSE_OR_POP, der die Sequenz um eine Anweisung von 13 auf 12 verkürzt. Coole Antwort - danke !!
Dave
@ Dave Es ist Python 2.6.6 (iPython)
Ashwini Chaudhary
22

Python macht spezielle Dinge mit verketteten Vergleichen.

Folgendes wird unterschiedlich bewertet:

x > y > z   # in this case, if x > y evaluates to true, then
            # the value of y is being used to compare, again,
            # to z

(x > y) > z # the parenth form, on the other hand, will first
            # evaluate x > y. And, compare the evaluated result
            # with z, which can be "True > z" or "False > z"

In beiden Fällen wird jedoch beim ersten Vergleich Falseder Rest der Aussage nicht berücksichtigt.

Für Ihren speziellen Fall

1 in [] in 'a'   # this is false because 1 is not in []

(1 in []) in a   # this gives an error because we are
                 # essentially doing this: False in 'a'

1 in ([] in 'a') # this fails because you cannot do
                 # [] in 'a'

Um die erste Regel oben zu demonstrieren, sind dies Anweisungen, die als True ausgewertet werden.

1 in [1,2] in [4,[1,2]] # But "1 in [4,[1,2]]" is False

2 < 4 > 1               # and note "2 < 1" is also not true

Vorrang von Python-Operatoren: http://docs.python.org/reference/expressions.html#summary

Alexander Chen
quelle
11

Aus der Dokumentation:

Vergleiche können beliebig verkettet werden, z. B. ist x <y <= z äquivalent zu x <y und y <= z, außer dass y nur einmal ausgewertet wird (aber in beiden Fällen wird z überhaupt nicht ausgewertet, wenn x <y gefunden wird falsch sein).

Dies bedeutet, dass es keine Assoziativität in gibt x in y in z !

Folgendes ist äquivalent:

1 in  []  in 'a'
# <=>
middle = []
#            False          not evaluated
result = (1 in middle) and (middle in 'a')


(1 in  []) in 'a'
# <=>
lhs = (1 in []) # False
result = lhs in 'a' # False in 'a' - TypeError


1 in  ([] in 'a')
# <=>
rhs = ([] in 'a') # TypeError
result = 1 in rhs
phant0m
quelle
3

Die kurze Antwort, da die lange hier bereits mehrmals und auf hervorragende Weise gegeben wird, ist, dass der boolesche Ausdruck kurzgeschlossen ist. Dies hat die Auswertung gestoppt, wenn eine Änderung von wahr in falsch oder umgekehrt nicht durch weitere Auswertung erfolgen kann.

(siehe http://en.wikipedia.org/wiki/Short-circuit_evaluation )

Es mag als Antwort etwas kurz sein (kein Wortspiel beabsichtigt), aber wie bereits erwähnt, sind alle anderen Erklärungen hier bereits recht gut gemacht, aber ich dachte, der Begriff hätte es verdient, erwähnt zu werden.

Peter
quelle