while utiliza que para for español ejemplos con ciclo bucle anidado python loops for-loop while-loop

utiliza - ¿Cómo puedo entender la cláusula `else` de los bucles Python?



for python español (14)

Muchos programadores de Python probablemente no son conscientes de que la sintaxis de los bucles while y for loops incluye una opción opcional else: cláusula:

for val in iterable: do_something(val) else: clean_up()

El cuerpo de la cláusula else es un buen lugar para ciertos tipos de acciones de limpieza, y se ejecuta en la terminación normal del ciclo: es decir, al salir del ciclo con return o break salta la cláusula else ; salir después de continue ejecuta. Sé esto solo porque lo busqué (una vez más), porque nunca puedo recordar cuándo se ejecuta la cláusula else .

¿Siempre? En "falla" del bucle, como su nombre indica? En la terminación regular? ¿Incluso si se sale del bucle con return ? Nunca puedo estar completamente seguro sin buscarlo.

Culpo mi persistente incertidumbre a la elección de la palabra clave: encuentro else increíblemente poco nemónica para esta semántica. Mi pregunta no es "por qué se usa esta palabra clave para este propósito" (que probablemente votaría para cerrar, aunque solo después de leer las respuestas y comentarios), sino cómo puedo pensar en la palabra clave else para que su semántica tenga sentido, y Por lo tanto, puedo recordarlo?

Estoy seguro de que hubo una buena cantidad de discusión sobre esto, y me imagino que la elección se hizo por coherencia con la declaración de try else: cláusula (que también tengo que buscar), y con el objetivo de no agregar a la lista de las palabras reservadas de Python. Quizás las razones para elegir else aclararán su función y la harán más memorable, pero busco conectar el nombre con la función, no después de una explicación histórica per se.

Las respuestas a esta pregunta , que mi pregunta se cerró brevemente como un duplicado, contienen muchas historias interesantes. Mi pregunta tiene un enfoque diferente (cómo conectar la semántica específica de else con la elección de la palabra clave), pero creo que debería haber un enlace a esta pregunta en alguna parte.


¿Cuándo un if ejecuta a else ? Cuando su condición es falsa. Es exactamente lo mismo para el while / else . Por lo tanto, puede pensar en while / else como un if que sigue ejecutando su verdadera condición hasta que evalúe false. Un break no cambia eso. Simplemente salta del bucle contenedor sin evaluación. El else solo se ejecuta si la evaluación de la condición if / while es falsa.

El for es similar, excepto que su condición falsa está agotando su iterador.

continue y break no ejecutar else . Esa no es su función. La break sale del bucle contenedor. La continue vuelve a la parte superior del bucle contenedor, donde se evalúa la condición del bucle. Es el acto de evaluar if / while es falso (o for no tiene más elementos) lo que se ejecuta de else y de ninguna otra manera.


(Esto está inspirado en la respuesta de @Mark Tolonen).

Una instrucción if ejecuta su cláusula else si su condición se evalúa como falsa. De manera idéntica, un ciclo while ejecuta la cláusula else si su condición se evalúa como falsa.

Esta regla coincide con el comportamiento que describió:

  • En la ejecución normal, el ciclo while se ejecuta repetidamente hasta que la condición se evalúa como falsa y, por lo tanto, al salir naturalmente del ciclo se ejecuta la cláusula else.
  • Cuando ejecuta una declaración de break , sale del bucle sin evaluar la condición, por lo que la condición no se puede evaluar como falsa y nunca ejecuta la cláusula else.
  • Cuando ejecuta una instrucción de continue , evalúa la condición nuevamente y hace exactamente lo que normalmente haría al comienzo de una iteración de bucle. Por lo tanto, si la condición es verdadera, sigue en bucle, pero si es falsa, ejecuta la cláusula else.
  • Otros métodos para salir del bucle, como return , no evalúan la condición y, por lo tanto, no ejecutan la cláusula else.

for bucles se comportan de la misma manera. Simplemente considere la condición como verdadera si el iterador tiene más elementos, o falso en caso contrario.


A mi modo de ver, de lo else: dispara cuando iteras más allá del final del bucle.

Si se break return o raise , no itera más allá del final del ciclo, se detiene de inmediato y, por lo tanto, lo else: bloque no se ejecutará. Si continue , todavía itera más allá del final del ciclo, ya que continuar solo salta a la siguiente iteración. No detiene el ciclo.


A mi modo de verlo, la clave es considerar el significado de continue lugar de else .

Las otras palabras clave que menciona se rompen del bucle (salen anormalmente) mientras que continue no lo hace, simplemente omite el resto del bloque de código dentro del bucle. El hecho de que pueda preceder a la terminación del ciclo es incidental: la terminación se realiza de la manera normal mediante la evaluación de la expresión condicional del ciclo.

Entonces solo necesita recordar que la cláusula else se ejecuta después de la terminación normal del bucle.


En el desarrollo impulsado por pruebas (TDD), cuando se usa el paradigma Premisa de prioridad de transformación , los bucles se tratan como una generalización de declaraciones condicionales.

Este enfoque combina bien con esta sintaxis, si considera solo declaraciones simples if/else (no elif ):

if cond: # 1 else: # 2

generaliza a:

while cond: # <-- generalization # 1 else: # 2

bien.

En otros idiomas, los pasos de TDD de un solo caso a casos con colecciones requieren más refactorización.

Aquí hay un ejemplo del blog 8thlight :

En el artículo vinculado en el blog 8thlight, se considera el kata de Word Wrap: agregar saltos de línea a las cadenas (la variable s en los fragmentos a continuación) para que se ajusten a un ancho determinado (la variable de length en los fragmentos a continuación). En un punto, la implementación tiene el siguiente aspecto (Java):

String result = ""; if (s.length() > length) { result = s.substring(0, length) + "/n" + s.substring(length); } else { result = s; } return result;

y la próxima prueba, que actualmente falla es:

@Test public void WordLongerThanTwiceLengthShouldBreakTwice() throws Exception { assertThat(wrap("verylongword", 4), is("very/nlong/nword")); }

Entonces tenemos un código que funciona condicionalmente: cuando se cumple una condición particular, se agrega un salto de línea. Queremos mejorar el código para manejar múltiples saltos de línea. La solución presentada en el artículo propone aplicar la transformación (if-> while) , sin embargo, el autor hace un comentario que:

Si bien los bucles no pueden tener cláusulas else , debemos eliminar la ruta else haciendo menos en la ruta if . Nuevamente, esto es una refactorización.

lo que obliga a hacer más cambios en el código en el contexto de una prueba fallida:

String result = ""; while (s.length() > length) { result += s.substring(0, length) + "/n"; s = s.substring(length); } result += s;

En TDD queremos escribir la menor cantidad de código posible para que las pruebas pasen. Gracias a la sintaxis de Python, es posible la siguiente transformación:

desde:

result = "" if len(s) > length: result = s[0:length] + "/n" s = s[length:] else: result += s

a:

result = "" while len(s) > length: result += s[0:length] + "/n" s = s[length:] else: result += s


Es mejor pensarlo de esta manera: el bloque else siempre se ejecutará si todo va bien en el bloque anterior for que se agote.

Justo en este contexto significará sin exception , sin break , sin return . Cualquier declaración que secuestre el control de for hará que se omita el bloque else .

Se encuentra un caso de uso común cuando se busca un elemento en un iterable , para el cual la búsqueda se cancela cuando se encuentra el elemento o se levanta / imprime un indicador "not found" través del siguiente bloque else :

for items in basket: if isinstance(item, Egg): break else: print("No eggs in basket")

Una continue no secuestra el control de for , for lo que el control pasará a else después for se agote for .


Esto es lo que esencialmente significa:

for/while ...: if ...: break if there was a break: pass else: ...

Es una mejor manera de escribir este patrón común:

found = False for/while ...: if ...: found = True break if not found: ...

La cláusula else no se ejecutará si hay un return porque el return deja la función, como debe ser. La única excepción a lo que puede estar pensando es finally , cuyo propósito es asegurarse de que siempre se ejecute.

continue no tiene nada especial que ver con este asunto. Hace que la iteración actual del ciclo finalice, lo que puede suceder que finalice todo el ciclo, y claramente en ese caso el ciclo no terminó con una break .

try/else es similar:

try: ... except: ... if there was an exception: pass else: ...


Mi momento de sorpresa con la cláusula del bucle fue cuando estaba viendo una charla de Raymond Hettinger , quien contó una historia sobre cómo pensó que debería haberse llamado nobreak . Eche un vistazo al siguiente código, ¿qué cree que haría?

for i in range(10): if test(i): break # ... work with i nobreak: print(''Loop completed'')

¿Qué adivinarías que hace? Bueno, la parte que dice nobreak solo se ejecutará si no se golpea una declaración de break en el bucle.


Otros ya han explicado la mecánica de while/for...else , y la referencia del lenguaje Python 3 tiene la definición autorizada (ver while y for ), pero aquí está mi mnemónica personal, FWIW. Supongo que la clave para mí ha sido dividir esto en dos partes: una para comprender el significado de lo else en relación con el bucle condicional y otra para comprender el control del bucle.

Creo que es más fácil comenzar por comprender while...else :

while tenga más artículos, haga cosas, de lo else , si se acaba, haga esto

El for...else mnemónico es básicamente el mismo:

for cada artículo, haz cosas, pero si te quedas sin nada, haz esto

En ambos casos, la parte else solo se alcanza una vez que no hay más elementos para procesar, y el último elemento se ha procesado de manera regular (es decir, sin break o return ). Una continue simplemente regresa y ve si hay más elementos. Mi mnemotécnico para estas reglas se aplica tanto a while como for :

cuando se break o return , no hay nada else que hacer,
y cuando digo continue , eso es "vuelta al comienzo" para ti

- con "loop back to start" que significa, obviamente, el inicio del ciclo donde verificamos si hay más elementos en el iterable, por lo que, en lo que respecta a lo else , continue realmente no juega ningún papel en absoluto.


Piense en la cláusula else como parte de la construcción del bucle; break rompe por completo la construcción del bucle y, por lo tanto, omite la cláusula else .

Pero realmente, mi mapeo mental es simplemente que es la versión ''estructurada'' del patrón C / C ++:

for (...) { ... if (test) { goto done; } ... } ... done: ...

Entonces, cuando me encuentro for...else o lo escribo yo mismo, en lugar de entenderlo directamente , lo traduzco mentalmente a la comprensión anterior del patrón y luego calculo qué partes del mapa de sintaxis de Python a qué partes del patrón.

(Puse ''estructurado'' entre comillas de miedo porque la diferencia no es si el código es estructurado o no, sino simplemente si hay palabras clave y gramática dedicadas a la estructura particular)


Por lo general, tiendo a pensar en una estructura de bucle como esta:

for item in my_sequence: if logic(item): do_something(item) break

Para parecerse mucho a un número variable de sentencias if/elif :

if logic(my_seq[0]): do_something(my_seq[0]) elif logic(my_seq[1]): do_something(my_seq[1]) elif logic(my_seq[2]): do_something(my_seq[2]) .... elif logic(my_seq[-1]): do_something(my_seq[-1])

En este caso, la instrucción else en el bucle for funciona exactamente igual que la instrucción else en la cadena de elif s, solo se ejecuta si ninguna de las condiciones antes de evaluar a True. (o interrumpir la ejecución con return o una excepción) Si mi bucle no se ajusta a esta especificación, por lo general, elijo dejar de usarlo for: else por la razón exacta por la que publicó esta pregunta: no es intuitivo.


Si intentas emparejar else con for en tu mente, podría ser confuso. No creo que la palabra clave else una gran opción para esta sintaxis, pero si la emparejas con break , puedes ver que realmente tiene sentido.

Déjame demostrarlo en lenguaje humano.

for cada persona en un grupo de sospechosos, if alguien es el criminal, break la investigación. else informe de falla.


else es apenas útil si de todos modos no hubo break en el ciclo for .


Si piensa en sus bucles como una estructura similar a esta (algo de pseudocódigo):

loop: if condition then ... //execute body goto loop else ...

podría tener un poco más de sentido. Un ciclo es esencialmente solo una declaración if que se repite hasta que la condición sea false . Y este es el punto importante. El bucle verifica su condición y ve que es false , por lo tanto ejecuta el else (como if/else un if/else normal) y luego el bucle está hecho.

Observe que lo else solo se ejecuta cuando se verifica la condición . Eso significa que si sale del cuerpo del bucle en medio de la ejecución con, por ejemplo, un return o una break , ya que la condición no se vuelve a verificar, el caso de lo else no se ejecutará.

Una continue por otro lado, detiene la ejecución actual y luego salta hacia atrás para verificar la condición del bucle nuevamente, razón por la cual se puede alcanzar el else en este escenario.


# tested in Python 3.6.4 def buy_fruit(fruits): ''''''I translate the ''else'' below into ''if no break'' from for loop '''''' for fruit in fruits: if ''rotten'' in fruit: print(f''do not want to buy {fruit}'') break else: #if no break print(f''ready to buy {fruits}'') if __name__ == ''__main__'': a_bag_of_apples = [''golden delicious'', ''honeycrisp'', ''rotten mcintosh''] b_bag_of_apples = [''granny smith'', ''red delicious'', ''honeycrisp'', ''gala'', ''fuji''] buy_fruit(a_bag_of_apples) buy_fruit(b_bag_of_apples) '''''' do not want to buy rotten mcintosh ready to buy [''granny smith'', ''red delicious'', ''honeycrisp'', ''gala'', ''fuji''] ''''''