2012-09-30 21 views
108

estoy haciendo un analizador de Python, y esto es realmente me confunde:Asociatividad de "en" en Python?

>>> 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 

¿Cómo funciona exactamente "en el" trabajo en Python, con respecto a la asociatividad, etc.?

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

+6

Probablemente esté siguiendo el comportamiento descrito aquí: http://docs.python.org/reference/expressions.html#not-in, el que le permite escribir 'if a millimoose

+3

@millimoose: Sí, nunca pensé en 'in' como un operador de" comparación ", supongo. : \ – Mehrdad

+0

Hola [¿Qué tipo de analizador se utiliza en el intérprete de Python?] (Http://stackoverflow.com/questions/15532616/what-type-of-parser-is-used-in-python-interpreter) –

Respuesta

123

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') en realidad nunca se evalúa, por lo que no se genera ningún error.

Estas son las definiciones de los estados:

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   
+0

Whoa! ! +1 ¡Es increíble, muchas gracias! ¡Parece realmente útil, si tan solo lo supiera! ¿Sabes dónde está esto en la documentación? I [miré] (http://docs.python.org/reference/expressions.html#boolean-operations) pero no pude encontrar nada que sugiriera esto. – Mehrdad

+1

'false' podría usarse en lugar de 'wrong' aquí. – jfs

+1

@ J.F.Sebastian gracias, arregló eso. –

22

Python hace cosas especiales con las comparaciones encadenados.

La siguiente se evalúan de forma 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" 

En ambos casos, sin embargo, si la primera comparación es False, el resto de la declaración no se observó.

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, se trata de declaraciones que se evalúan como True.

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 operadores Python: http://docs.python.org/reference/expressions.html#summary

11

From the documentation:

Las comparaciones se pueden encadenar arbitrariamente, por ejemplo, X < y < = z es equivalente a x < Y e Y < = z, excepto que y se evalúa solo una vez (pero en ambos casos z no se evalúa cuando x < y es falso).

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 
3

La respuesta corta, ya que el tiempo que uno ya se da varias veces aquí y en excelentes maneras, es que la expresión booleana es cortocircuitado, esto es tiene detuvo la evaluación cuando un cambio de verdadero en falso o viceversa no puede ocurrir mediante una evaluación adicional.

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

Podría ser un poco corto (sin doble sentido) como una respuesta, pero como se mencionó, el resto de la explicación se allready hecho bastante bien aquí, pero pensé que el término merece ser mencionado .