La asociatividad de “en” en Python?

Estoy haciendo un analizador de Python, y esto realmente me confunde:

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

¿Cómo funciona exactamente “in” en Python, con respecto a la asociatividad, etc.?

¿Por qué dos de estas expresiones no se comportan de la misma manera?

1 in [] in 'a' se evalúa como (1 in []) and ([] in 'a') .

Dado que la primera condición ( 1 in [] ) es False , toda la condición se evalúa como False ; ([] in 'a') nunca se evalúa realmente, por lo que no se genera ningún error.

Aquí están las definiciones de la statement:

 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 

Python hace cosas especiales con comparaciones encadenadas.

Los siguientes son evaluados de manera diferente:

 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" 

Sin embargo, en ambos casos, si la primera comparación es False , el rest de la statement no se verá.

Para su caso particular,

 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' 

También para demostrar la primera regla anterior, estas son declaraciones que se evalúan como Verdaderas.

 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 

Precedencia de los operadores de Python: http://docs.python.org/reference/expressions.html#summary

De la documentación:

Las comparaciones se pueden encadenar arbitrariamente, por ejemplo, x

Lo que esto significa es que no hay asociatividad en x in y in z !

Los siguientes son equivalentes:

 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 

La respuesta corta, ya que la larga ya se ha dado varias veces aquí y de manera excelente, es que la expresión booleana está en cortocircuito , esto ha detenido la evaluación cuando un cambio de verdadero en falso o viceversa no puede suceder mediante una evaluación adicional.

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

Puede que sea un poco corto (sin juego de palabras) como respuesta, pero como se mencionó, todas las demás explicaciones ya se han hecho bastante bien aquí, pero pensé que el término merecía ser mencionado.