Esta es más una cuestión conceptual. Recientemente vi un fragmento de código en Python (funcionó en 2.7, y también podría haberse ejecutado en 2.5) en el que un for
bucle usaba el mismo nombre tanto para la lista que se estaba iterando como para el elemento en la lista , lo que me parece una mala práctica y algo que no debería funcionar en absoluto.
Por ejemplo:
x = [1,2,3,4,5]
for x in x:
print x
print x
Rendimientos:
1
2
3
4
5
5
Ahora, tiene sentido para mí que el último valor impreso sería el último valor asignado ax desde el ciclo, pero no entiendo por qué podría usar el mismo nombre de variable para ambas partes del for
ciclo y tener Funciona según lo previsto. ¿Están en diferentes ámbitos? ¿Qué está pasando debajo del capó que permite que algo como esto funcione?
for i in printAndReturn [1,2,3,4,5] …
, ¿cuántas veces se deben[1,2,3,4,5]
imprimir?for
bucle tienen el mismo alcance.for ($x as $x)
pero es un código feo IMORespuestas:
Que
dis
nos dice:Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from dis import dis >>> dis("""x = [1,2,3,4,5] ... for x in x: ... print(x) ... print(x)""") 1 0 LOAD_CONST 0 (1) 3 LOAD_CONST 1 (2) 6 LOAD_CONST 2 (3) 9 LOAD_CONST 3 (4) 12 LOAD_CONST 4 (5) 15 BUILD_LIST 5 18 STORE_NAME 0 (x) 2 21 SETUP_LOOP 24 (to 48) 24 LOAD_NAME 0 (x) 27 GET_ITER >> 28 FOR_ITER 16 (to 47) 31 STORE_NAME 0 (x) 3 34 LOAD_NAME 1 (print) 37 LOAD_NAME 0 (x) 40 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 43 POP_TOP 44 JUMP_ABSOLUTE 28 >> 47 POP_BLOCK 4 >> 48 LOAD_NAME 1 (print) 51 LOAD_NAME 0 (x) 54 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 57 POP_TOP 58 LOAD_CONST 5 (None) 61 RETURN_VALUE
Los bits clave son las secciones 2 y 3: cargamos el valor de
x
(24 LOAD_NAME 0 (x)
) y luego obtenemos su iterador (27 GET_ITER
) y comenzamos a iterar sobre él (28 FOR_ITER
). Python nunca vuelve a cargar el iterador nuevamente .Aparte: no tendría ningún sentido hacerlo, ya que ya tiene el iterador, y como señala Abhijit en su respuesta , la Sección 7.3 de la especificación de Python realmente requiere este comportamiento).
Cuando el nombre
x
se sobrescribe para apuntar a cada valor dentro de la lista anteriormente conocida comox
Python no tiene problemas para encontrar el iterador porque nunca necesita mirar el nombrex
nuevamente para finalizar el protocolo de iteración.fuente
Usando su código de ejemplo como referencia principal
x = [1,2,3,4,5] for x in x: print x print x
Me gustaría que hiciera referencia a la sección 7.3. La declaración for en el manual
Extracto 1
Lo que significa es que su variable
x
, que es un nombre simbólico de un objetolist
:[1,2,3,4,5]
se evalúa como un objeto iterable. Incluso si la variable, la referencia simbólica cambia su lealtad, ya que la lista de expresiones no se evalúa nuevamente, no hay impacto en el objeto iterable que ya ha sido evaluado y generado.Nota
Extracto 2
Aquí, la suite se refiere al iterador y no a la lista de expresiones. Entonces, para cada iteración, el iterador se ejecuta para producir el siguiente elemento en lugar de referirse a la lista de expresiones original.
fuente
Es necesario que funcione de esta manera, si lo piensa. La expresión de la secuencia de un
for
bucle podría ser cualquier cosa:binaryfile = open("file", "rb") for byte in binaryfile.read(5): ...
No podemos consultar la secuencia en cada paso a través del ciclo, o aquí terminaríamos leyendo del siguiente lote de 5 bytes la segunda vez. Naturalmente, Python debe almacenar de alguna manera el resultado de la expresión de forma privada antes de que comience el ciclo.
No. Para confirmar esto, puede mantener una referencia al diccionario de alcance original ( locals () ) y notar que, de hecho, está usando las mismas variables dentro del ciclo:
x = [1,2,3,4,5] loc = locals() for x in x: print locals() is loc # True print loc["x"] # 1 break
Sean Vieira mostró exactamente lo que está sucediendo bajo el capó, pero para describirlo en un código Python más legible, su
for
ciclo es esencialmente equivalente a estewhile
ciclo:it = iter(x) while True: try: x = it.next() except StopIteration: break print x
Esto es diferente del enfoque tradicional de indexación para la iteración que vería en versiones anteriores de Java, por ejemplo:
for (int index = 0; index < x.length; index++) { x = x[index]; ... }
Este enfoque fallaría cuando la variable de elemento y la variable de secuencia son iguales, porque la secuencia
x
ya no estaría disponible para buscar el índice siguiente después de que la primera vezx
se reasigne al primer elemento.Sin embargo, con el primer enfoque, la primera línea (
it = iter(x)
) solicita un objeto iterador que es realmente responsable de proporcionar el siguiente elemento a partir de ese momento.x
Ya no es necesario acceder directamente a la secuencia a la que apuntaba originalmente.fuente
Es la diferencia entre una variable (x) y el objeto al que apunta (la lista). Cuando comienza el ciclo for, Python toma una referencia interna al objeto al que apunta x. Utiliza el objeto y no lo que x hace referencia en un momento dado.
Si reasigna x, el ciclo for no cambia. Si x apunta a un objeto mutable (p. Ej., Una lista) y usted cambia ese objeto (p. Ej., Elimina un elemento), los resultados pueden ser impredecibles.
fuente
Básicamente, el ciclo for toma la lista
x
y luego, almacenándola como una variable temporal, vuelve a asignarx
a cada valor en esa variable temporal. Por lo tanto,x
ahora es el último valor de la lista.>>> x = [1, 2, 3] >>> [x for x in x] [1, 2, 3] >>> x 3 >>>
Como en esto:
>>> def foo(bar): ... return bar ... >>> x = [1, 2, 3] >>> for x in foo(x): ... print x ... 1 2 3 >>>
En este ejemplo,
x
se almacenafoo()
comobar
, por lo que, aunquex
se está reasignando, todavía existe (ed) enfoo()
para que podamos usarlo para activar nuestrofor
bucle.fuente
x
esté siendo reasignado. Sebar
crea una variable localfoo
y se le asigna el valor dex
.foo
luego devuelve ese valor en forma de un objeto que se usa en lafor
condición. Por tanto, la variablex
nunca se reasignó en el segundo ejemplo. Aunque estoy de acuerdo con el primero.x
embargo, @Tonio sigue siendo la variable de iteración y, por lo tanto, toma un nuevo valor para cada ciclo. Después del bucle,x
es igual a3
en ambos casos.x
mantenga3
ynot
[1,2,3] `?x
ya no se refiere a lax
lista original , por lo que no hay confusión. Básicamente, Python recuerda que está iterando sobre lax
lista original , pero tan pronto como comienzas a asignar el valor de iteración (0,1,2, etc.) al nombrex
, ya no se refiere a lax
lista original . El nombre se reasigna al valor de iteración.In [1]: x = range(5) In [2]: x Out[2]: [0, 1, 2, 3, 4] In [3]: id(x) Out[3]: 4371091680 In [4]: for x in x: ...: print id(x), x ...: 140470424504688 0 140470424504664 1 140470424504640 2 140470424504616 3 140470424504592 4 In [5]: id(x) Out[5]: 140470424504592
fuente
x
simplemente deja de referirse a la lista de rangos y en su lugar se le asignan los nuevos valores de iteración. La lista de rangos todavía existe intacta. Si observa el valor dex
after the loop, será4
x
nunca se mencionax
;x
referido a una secuencia. Luego se refirió1
, luego a2
, etc.