¿Por qué Python usa 'else' después de los bucles for y while?

Resuelto Kent Boogaart asked hace 12 años • 25 respuestas

Entiendo cómo funciona esta construcción:

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break
else:
    print("Completed successfully")

Pero no entiendo por qué elsese usa como palabra clave aquí, ya que sugiere que el código en cuestión solo se ejecuta si el forbloque no se completa, ¡que es lo opuesto a lo que hace! No importa cómo lo piense, mi cerebro no puede pasar sin problemas de la fordeclaración al elsebloque. Para mí, continueo continuewithtendría más sentido (y estoy tratando de entrenarme para leerlo como tal).

Me pregunto cómo los programadores de Python leen esta construcción en su cabeza (o en voz alta, si lo desea). ¿Quizás me falta algo que haría que dichos bloques de código sean más fácilmente descifrables?


Esta pregunta trata sobre la decisión de diseño subyacente , es decir, por qué es útil poder escribir este código. Consulte también la cláusula Else en la declaración while de Python para la pregunta específica sobre lo que significa la sintaxis .

Kent Boogaart avatar Apr 02 '12 23:04 Kent Boogaart
Aceptado

Una construcción común es ejecutar un bucle hasta que se encuentre algo y luego salir del bucle. El problema es que si salgo del bucle o el bucle termina, necesito determinar qué caso ocurrió. Un método es crear una bandera o una variable de tienda que me permita hacer una segunda prueba para ver cómo se salió del bucle.

Por ejemplo, supongamos que necesito buscar en una lista y procesar cada elemento hasta encontrar un elemento marcado y luego detener el procesamiento. Si falta el elemento de bandera, entonces se debe generar una excepción.

Usando la construcción Python for... elsetienes

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

Compare esto con un método que no utiliza este azúcar sintáctico:

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

En el primer caso, raiseestá estrechamente vinculado al bucle for con el que trabaja. En el segundo la unión no es tan fuerte y pueden introducirse errores durante el mantenimiento.

Lance Helsten avatar Apr 02 '2012 17:04 Lance Helsten

Es una construcción extraña incluso para los programadores Python experimentados. Cuando se usa junto con bucles for, básicamente significa "buscar algún elemento en el iterable; de ​​lo contrario, si no se encuentra ninguno, haga ...". Como en:

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

Pero cada vez que veas esta construcción, una mejor alternativa es encapsular la búsqueda en una función:

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

O utilice una lista por comprensión:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

No es semánticamente equivalente a las otras dos versiones, pero funciona bastante bien en código que no es crítico para el rendimiento, donde no importa si se itera toda la lista o no. Otros pueden no estar de acuerdo, pero yo personalmente evitaría usar los bloques for-else o while-else en el código de producción.

Véase también [Python-ideas] Resumen de for...else hilos

Stand with Gaza avatar Apr 02 '2012 16:04 Stand with Gaza

Hay una excelente presentación de Raymond Hettinger, titulada Transforming Code into Beautiful, Idiomatic Python , en la que aborda brevemente la historia de la for ... elseconstrucción. La sección relevante es "Distinguir múltiples puntos de salida en bucles" que comienza a las 15:50 y continúa durante unos tres minutos. Aquí están los puntos destacados:

  • La for ... elseconstrucción fue ideada por Donald Knuth como reemplazo para ciertos GOTOcasos de uso;
  • Reutilizar la elsepalabra clave tenía sentido porque "es lo que usó Knuth, y la gente sabía, en ese momento, que todas [ forlas declaraciones] tenían incrustado un ify GOTOdebajo, y esperaban el else;"
  • En retrospectiva, debería haberse llamado "sin interrupción" (o posiblemente "sin interrupción"), y así no sería confuso.*

Entonces, si la pregunta es "¿Por qué no cambian esta palabra clave?" entonces Cat Plus Plus probablemente dio la respuesta más precisa : en este punto, sería demasiado destructivo para el código existente para ser práctico. Pero si la pregunta que realmente te haces es por qué elsese reutilizó en primer lugar, bueno, aparentemente parecía una buena idea en ese momento.

Personalmente, me gusta el compromiso de comentar # no breaken línea dondequiera que else, a simple vista, se pueda confundir con algo que pertenece al bucle. Es razonablemente claro y conciso. Esta opción recibe una breve mención en el resumen que Bjorn vinculó al final de su respuesta:

Para completar, debo mencionar que con un ligero cambio en la sintaxis, los programadores que quieran esta sintaxis pueden tenerla ahora mismo:

for item in sequence:
    process(item)
else:  # no break
    suite

* Cita adicional de esa parte del vídeo: "Al igual que si llamaramos a lambda makefunction, nadie preguntaría: '¿Qué hace lambda?'"

Air avatar May 19 '2014 22:05 Air

Para hacerlo simple, puedes pensar en ello así;

  • Si encuentra el breakcomando en el forbucle, elseno se llamará la pieza.
  • Si no encuentra el breakcomando en el forbucle, elsese llamará a la pieza.

En otras palabras, si la iteración del bucle for no se "rompe" con break, elsese llamará a la parte.

Ad Infinitum avatar Oct 27 '2017 11:10 Ad Infinitum

Porque no querían introducir una nueva palabra clave en el idioma. Cada uno roba un identificador y causa problemas de compatibilidad con versiones anteriores, por lo que suele ser el último recurso.

Cat Plus Plus avatar Apr 02 '2012 16:04 Cat Plus Plus