2012-08-07 20 views
7

Recientemente he estado experimentando con generadores de pitón un poco, y me encontré con el siguiente comportamiento curioso, y tengo curiosidad por entender por qué sucede esto y qué está pasando:Python - comportamiento curioso/inesperado - precedencia de los operadores

def generating_test(n): 
    for a in range(n): 
     yield "a squared is %s" % a*a # Notice instead of a**2 we have written a*a 

for asquare in generating_test(3): 
    print asquare 

salida:

a squared is 1 
a squared is 2a squared is 2 

Versus el siguiente script que genera la salida esperada:

def generating_test(n): 
    for a in range(n): 
     yield "a squared is %s" % a**2 # we use the correct a**2 here 

for asquare in generating_test(3): 
    print asquare 

Salida:

a squared is 0 
a squared is 1 
a squared is 4 
+1

aside: si realmente está formateando un entero, use '% d', no'% s'. – kojiro

+5

O acepte la nueva sintaxis de 'format'. Pensé que era un poco largo cuando lo vi por primera vez pero me ha gustado. – DSM

+0

Como me dijo una vez un compañero de trabajo, * siempre * uso una tupla después de '%' – chepner

Respuesta

20

Esto no tiene nada que ver con los generadores:

>>> a = 2 
>>> "a squared is %s" % a 
'a squared is 2' 
>>> ("a squared is %s" % a)*a 
'a squared is 2a squared is 2' 
>>> "a squared is %s" % a*a 
'a squared is 2a squared is 2' 
>>> "a squared is %s" % (a*a) 
'a squared is 4' 

El % op se lleva a cabo antes de la multiplicación, utilizando la cadena y el primer a como argumentos. Su a**2 funciona porque el ** op con a y 2 como argumentos se evalúa antes del %.

+0

¡Eso fue rápido! – jamylak

+2

yeh me gane ... apenas: P –

+0

Muy interesante, gracias por la respuesta rápida - Todavía tengo que esperar 11 minutos antes de poder marcar como aceptado. He eliminado las etiquetas de los generadores y el rendimiento ya que no son relevantes. –

8

Python's order of operations es de izquierda a derecha, excepto cuando se aplica PEMDAS. El operador de interpolación de cadenas aparentemente tiene la misma precedencia que de módulo y la multiplicación, ya que si se invierte el orden, por lo que la multiplicación a la izquierda de la interpolación, éste tendrá prioridad:

>>> print 3 * "a foo %s" % 'hi' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: not enough arguments for format string 
>>> print 3 * "a foo %s" % ('hi', 'ho', 'yo') 
a foo hia foo hoa foo yo 

Sin embargo, a medida que has demostrado, triunfos de exponenciación orden de izquierda a derecha.

Actualización: En ese same document under Binary Arithmetic Operations se afirma algo denotativamente obvio, pero connotativamente relevante:

... el operador% también está sobrecargado por la cadena Unicode y objetos para realizar formato de cadenas (también conocido como interpolación).

Mientras que parece que acaba de estar diciendo lo que el operador % hace, creo que su ubicación y el contexto también se dice que tiene la misma precedencia si se utiliza como módulo o interpolar.

+2

El resumen al final de ese documento lo expresa más explícitamente, en particular, la nota 8 dice: "El operador'% 'también se utiliza para el formato de cadenas; se aplica la misma precedencia". – lvc

+0

Imagine si fuera de otra manera ... ¡no podría analizar el código de Python hasta que no supiera los tipos de tiempo de ejecución de las subexpresiones! – Aaron

+0

@ Aaron * Las cosas serían tan diferentes si no fueran como son. * - [Anna Russell] (http://en.wikipedia.org/wiki/Anna_Russell). Si el tipo [anotaciones] (http://www.python.org/dev/peps/pep-3107/) fuera requerido por python y se utilizara para forzar el tipo en tiempo de compilación, sería un lenguaje diferente, pero no una idea loca. . :) – kojiro

5

Cuando observa un comportamiento inesperado, comience su análisis destilándolo al caso más simple posible. Un caso simple será más fácil de estudiar y comprender.

El comportamiento inesperado:

>>> 'hello %s' % 3 * 2 
'hello 3hello 3' 

(Se espera que 'hello 6')


Estamos razón de Python se debe interpretar el comando como 'hello 3' * 2 en lugar de 'hello %d' % 6.Intentamos forzar la segunda interpretación con corchetes

>>> "hello %s" % (3*2) 
'hello 6' 

Eureka!

Hemos demostrado que el operador de formato de cadenas % tiene una precedencia mayor o igual que la multiplicación. Comprobamos la documentación de Python - sí, confirma esta http://docs.python.org/reference/expressions.html#summary

Para confirmar que la prioridad es igual, podemos intentarlo al revés:

>>> "%d,"*2%(1,2) 
'1,2,' 

Al ver que la coma (,) se duplica, se razón por la que la multiplicación "%d," * 2 se realizó antes del formato de cadena %. Si la multiplicación puede preceder al formato de cadena, y el formato de cadena antecede a la multiplicación, deben ser iguales en precedencia.

Cuestiones relacionadas