Asignación de tuplas de Python y verificación en sentencias condicionales

Así que me encontré con un comportamiento particular de tuplas en python que me preguntaba si hay alguna razón en particular para que esto suceda.

Si bien somos perfectamente capaces de asignar una tupla a una variable sin encerrarla explícitamente entre paréntesis:

>>> foo_bar_tuple = "foo","bar" >>> 

no podemos imprimir o verificar en una statement condicional if la variable que contiene la tupla de la manera anterior (sin escribir explícitamente los paréntesis):

 >>> print foo_bar_tuple == "foo","bar" False bar >>> if foo_bar_tuple == "foo","bar": pass SyntaxError: invalid syntax >>> >>> print foo_bar_tuple == ("foo","bar") True >>> >>> if foo_bar_tuple == ("foo","bar"): pass >>> 

¿Alguien por qué? Gracias de antemano y aunque no encontré ningún tema similar, por favor, infórmeme si cree que es una posible publicación. Saludos alex

Es porque las expresiones separadas por comas se evalúan antes de toda la tupla separada por comas (que es una “lista de expresiones” en la terminología de la gramática de Python). Entonces, cuando haces foo_bar_tuple=="foo", "bar" , eso se interpreta como (foo_bar_tuple=="foo"), "bar" . Este comportamiento se describe en la documentación .

Puedes ver esto si solo escribes tal expresión por sí misma:

 >>> 1, 2 == 1, 2 # interpreted as "1, (2==1), 2" (1, False, 2) 

El SyntaxError para la tupla sin paréntesis se debe a que una tupla sin paréntesis no es un “átomo” en la gramática de Python, lo que significa que no es válido como el contenido exclusivo de una condición if . (Puedes verificar esto por ti mismo siguiendo la gramática ).

Teniendo en cuenta un ejemplo de if 1 == 1,2: que debería causar SyntaxError , siguiendo la gramática completa :

 if 1 == 1,2: 

Al usar if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] , podemos cambiar la palabra clave if y comenzar a analizar 1 == 1,2:

Para la regla de test , solo coinciden las primeras producciones:

 test: or_test ['if' or_test 'else' test] | lambdef 

Entonces obtenemos:

 or_test: and_test ('or' and_test)* 

Y bajar en and_test :

 and_test: not_test ('and' not_test)* 

Aquí simplemente not_test en not_test en este momento:

 not_test: 'not' not_test | comparison 

Tenga en cuenta que nuestra entrada es 1 == 1,2: tanto, la primera producción no coincide y verificamos la otra: (1)

 comparison: expr (comp_op expr)* 

Continuando con la reducción (tomamos el único no terminal primero, ya que la estrella cero o más requiere un terminal que no tenemos en absoluto en nuestra entrada):

 expr: xor_expr ('|' xor_expr)* xor_expr: and_expr ('^' and_expr)* and_expr: shift_expr ('&' shift_expr)* shift_expr: arith_expr (('<<'|'>>') arith_expr)* arith_expr: term (('+'|'-') term)* term: factor (('*'|'/'|'%'|'//') factor)* factor: ('+'|'-'|'~') factor | power 

Ahora usamos la producción de power :

 power: atom trailer* ['**' factor] atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') 

Y cambia NUMBER ( 1 en nuestra entrada) y reduce. Ahora estamos de vuelta en (1) con la entrada ==1,2: para analizar. == coincide con comp_op :

 comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' 

Así que lo cambiamos y reducimos, dejándonos con la entrada 1,2: (la salida del análisis actual es NUMBER comp_op , ahora necesitamos coincidir con expr ). Repetimos el proceso para el lado izquierdo, yendo directamente al atom no terminal del atom y seleccionando la producción de NUMBER . Desplazar y reducir.

Dado que , no coincide con ningún comp_op , reducimos la test no terminal y recibimos 'if' NUMBER comp_op NUMBER . Necesitamos coincidir con else , elif o : now, pero tenemos , así que fallamos con SyntaxError .

Creo que la tabla de precedencia del operador resume esto muy bien:

Verás que las comparaciones vienen antes que las expresiones, que en realidad son las últimas.

 in, not in, is, is not, Comparisons, including membership tests <, <=, >, >=, <>, !=, == and identity tests ... (expressions...), [expressions...], Binding or tuple display, list display, {key: value...}, `expressions...` dictionary display, string conversion