Alcance en bucles 'for' de Python


177

No estoy preguntando sobre las reglas de alcance de Python; En general, entiendo cómo funciona el alcance en Python para bucles. Mi pregunta es por qué las decisiones de diseño se tomaron de esta manera. Por ejemplo (sin juego de palabras):

for foo in xrange(10):
    bar = 2
print(foo, bar)

Lo anterior imprimirá (9,2).

Esto me parece extraño: 'foo' en realidad solo controla el bucle, y 'bar' se definió dentro del bucle. Puedo entender por qué podría ser necesario acceder a 'bar' fuera del bucle (de lo contrario, los bucles tendrían una funcionalidad muy limitada). Lo que no entiendo es por qué es necesario que la variable de control permanezca dentro del alcance después de que salga el bucle. En mi experiencia, simplemente abarrota el espacio de nombres global y hace que sea más difícil rastrear errores que los intérpretes podrían detectar en otros idiomas.


66
Si no desea que el forbucle atempere su espacio de nombres global, envuélvalo en una función. Cierres en abundancia!
jathanism

24
A menos que esté ejecutando un bucle en el espacio de nombres global (poco común), está abarrotando un espacio de nombres local .
Glenn Maynard el

3
Si esto no existiera, ¿cómo continuaría procesando más tarde en el punto que dejó dentro del bucle? ¿Solo define la variable de control antes del ciclo?
endolito el

9
@endolith Sí ... ¿Por qué no requerir eso?
Steven Lu

3
bueno, la gente preferirá lo que está acostumbrada a hacer. Diría que este tipo de cosas lastima al codificador de Python que se acostumbra a este tipo de cosas y tiene que pasar por un proceso doloroso al cambiar a un idioma diferente. Para el resto de nosotros, supongo que es un pequeño atajo ordenado.
Steven Lu

Respuestas:


107

La respuesta más probable es que simplemente mantiene la gramática simple, no ha sido un obstáculo para la adopción, y muchos han estado contentos de no tener que desambiguar el alcance al que pertenece un nombre al asignarlo dentro de una construcción de bucle. Las variables no se declaran dentro de un alcance, está implícito en la ubicación de las declaraciones de asignación. La globalpalabra clave existe solo por este motivo (para indicar que la asignación se realiza en un ámbito global).

Actualizar

Aquí hay una buena discusión sobre el tema: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

Las propuestas previas para hacer que las variables del bucle for sean locales en el bucle han tropezado con el problema del código existente que depende de que la variable del bucle mantenga su valor después de salir del bucle, y parece que esto se considera una característica deseable.

En resumen, probablemente puedas echarle la culpa a la comunidad de Python: P


2
¿Cómo sería más complicada la gramática si el alcance de la variable de inducción se limitara al cuerpo del bucle? Tal cambio se limitaría al análisis semántico en Python, no a su gramática.
Charles

66
Los bucles no son bloques en Python. Este tipo de cambio de comportamiento requeriría cambiar la gramática fundamentalmente o proporcionar un caso especial. El concepto completo de una variable de inducción tampoco se expresa en la gramática actual. La gramática proporciona el contrato de cómo interpretará el intérprete. Mi punto es que no puedo prever cómo se puede hacer un cambio en este comportamiento sin complicar la gramática. Todo es discutible ya que el efecto secundario de la decisión de diseño se ha convertido en una característica.
Jeremy Brown

1
Esta publicación aquí mail.python.org/pipermail/python-dev/2005-September/056677.html brinda más detalles sobre la velocidad y las complicaciones a las que alude el Sr. Brown.
rajesh

62

Python no tiene bloques, al igual que otros lenguajes (como C / C ++ o Java). Por lo tanto, la unidad de alcance en Python es una función.


3
Estoy confundido: ¿qué impide que Python alcance los bucles de la misma manera que las funciones tienen un alcance?
Quimeracoder

36
No es realmente cierto, es solo que la gramática no se vuelve loca. ( docs.python.org/reference/… ) "Un bloque es un fragmento de texto del programa Python que se ejecuta como una unidad. Los siguientes son bloques: un módulo, un cuerpo de función y una definición de clase ..."
Jeremy Brown

1
@thebackhand, nada. Simplemente se consideró innecesario.
habnabit

@ Jeremy Brown, de hecho. Buena nota.
atzz

66
@thebackhand: en idiomas con bloques, los forbucles de alcance son una extensión natural de un principio general. En Python tendría que ser un caso especial, y los casos especiales deben evitarse a menos que tengan beneficios convincentes.
atzz

39

Un caso realmente útil para esto es cuando se usa enumeratey desea el recuento total al final:

for count, x in enumerate(someiterator, start=1):
    dosomething(count, x)
print "I did something {0} times".format(count)

¿Es esto necesario? No. Pero seguro que es conveniente.

Otra cosa a tener en cuenta: en Python 2, las variables en las comprensiones de listas también se filtran:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9

Pero, lo mismo no se aplica a Python 3.


44
Podría haber hecho eso presumiblemente en la elsecláusula, es decir. else: print "I did something {0} times".format(count)- antes de que desaparezca el alcance local (que no existe en Python)
Nas Banov

3
Solo el segundo ejemplo no funciona en Python 3, ¿verdad? El primero todavía lo hace? ¿Notas sobre por qué se eliminó de Python 3?
endolito

77
para el recuento, elemento en enumerate (a, start = 1): # índice predeterminado es de cero
Tao Zhang

3
El primer ejemplo, en lugar de ser un buen caso de uso, parece más la evidencia de que esta regla de alcance es peligrosa y no se debe confiar en ella. ¿Qué pasa si someiteratorestá vacío?
max

1
@Nas Si bien se elsepodría usar una cláusula en este caso, no funcionaría en general ya que el cuerpo del bucle podría breakprematuramente.
jamesdlin

2

Si tiene una declaración de interrupción en el ciclo (y desea usar el valor de iteración más tarde, tal vez para recuperar, indexar algo o dar estado), le ahorra una línea de código y una asignación, por lo que es conveniente.


1

Una de las principales influencias para Python es ABC , un lenguaje desarrollado en los Países Bajos para enseñar conceptos de programación a principiantes. El creador de Python, Guido van Rossum, trabajó en ABC durante varios años en la década de 1980. No sé casi nada sobre ABC, pero como está destinado a principiantes, supongo que debe tener un número limitado de ámbitos, al igual que los primeros BASIC.


-1

Para empezar, si las variables fueran locales para los bucles, esos bucles serían inútiles para la mayoría de la programación del mundo real.

En la situación actual:

# Sum the values 0..9
total = 0
for foo in xrange(10):
    total = total + foo
print total

rendimientos 45. Ahora, considere cómo funciona la asignación en Python. Si las variables de bucle fueran estrictamente locales:

# Sum the values 0..9?
total = 0
for foo in xrange(10):
    # Create a new integer object with value "total + foo" and bind it to a new
    # loop-local variable named "total".
    total = total + foo
print total

cede 0, porque totaldentro del ciclo después de la asignación no es la misma variable que totalfuera del ciclo. Esto no sería un comportamiento óptimo o esperado.


55
No respondiendo la pregunta. El OP estaba preguntando acerca de foo, no total (o barra en su ejemplo).
James Bradbury

66
@JamesBradbury totaly footodavía tendría enlaces de bucle local en el escenario del OP y la lógica es la misma.
Kirk Strauser

2
OP: "Puedo entender por qué podría ser necesario acceder a 'bar' fuera del bucle (de lo contrario, los bucles tendrían una funcionalidad muy limitada). Lo que no entiendo es por qué es necesario que la variable de control permanezca en alcance después de que salga el bucle ". (énfasis mío)
James Bradbury

2
@JamesBradbury Puede que tengas razón, pero respondí esto hace tres años y probablemente no valga la pena debatirlo ahora.
Kirk Strauser
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.