Saltar al contenido

¿Cómo puedo limitar las iteraciones de un bucle en Python?

No olvides que en la informática un error casi siempere suele tener diferentes soluciones, pero nosotros aquí te enseñamos la mejor y más eficiente.

Solución:

¿Cómo puedo limitar las iteraciones de un bucle en Python?

for index, item in enumerate(items):
    print(item)
    if index == limit:
        break

¿Hay una forma más corta e idiomática de escribir lo anterior? ¿Cómo?

Incluyendo el índice

zip se detiene en el iterable más corto de sus argumentos. (En contraste con el comportamiento de zip_longestque usa el iterable más largo).

range puede proporcionar un iterable limitado que podemos pasar a zip junto con nuestro iterable principal.

Entonces podemos pasar un range objeto (con su stop argumento) a zip y usarlo como una enumeración limitada.

zip(range(limit), items)

Usando Python 3, zip y range devuelven iterables, que canalizan los datos en lugar de materializarlos en listas para pasos intermedios.

for index, item in zip(range(limit), items):
    print(index, item)

Para obtener el mismo comportamiento en Python 2, simplemente sustituya xrange por range y itertools.izip por zip.

from itertools import izip
for index, item in izip(xrange(limit), items):
    print(item)

Si no requiere el índice, itertools.islice

Puedes usar itertools.islice:

for item in itertools.islice(items, 0, stop):
    print(item)

que no requiere asignación al índice.

Composición enumerate(islice(items, stop)) para obtener el índice

Como señala Pablo Ruiz Ruiz, también podemos componer islice con enumerate.

for index, item in enumerate(islice(items, limit)):
    print(index, item)

¿Por qué no está integrado en enumerate?

Aquí está la enumeración implementada en Python puro (con posibles modificaciones para obtener el comportamiento deseado en los comentarios):

def enumerate(collection, start=0):  # could add stop=None
    i = start
    it = iter(collection)
    while 1:                         # could modify to `while i != stop:`
        yield (i, next(it))
        i += 1

Lo anterior tendría menos rendimiento para aquellos que ya usan enumerate, porque tendría que verificar si es hora de detener cada iteración. Podemos verificar y usar la enumeración anterior si no obtenemos un argumento de detención:

_enumerate = enumerate

def enumerate(collection, start=0, stop=None):
    if stop is not None:
        return zip(range(start, stop), collection)
    return _enumerate(collection, start)

Esta verificación adicional tendría un impacto leve e insignificante en el rendimiento.

En cuanto a por qué enumerar no tiene un argumento de parada, esto se propuso originalmente (ver PEP 279):

Esta función se propuso originalmente con argumentos opcionales de inicio y finalización. GvR [Guido van Rossum] señaló que la llamada a la función
enumerate(seqn, 4, 6) tenía una interpretación alternativa y plausible como un segmento que devolvería los elementos cuarto y quinto de la secuencia. Para evitar la ambigüedad, los argumentos opcionales se eliminaron aunque significaba perder flexibilidad como contador de bucles. Esa flexibilidad era más importante para el caso común de contar desde uno, como en:

for linenum, line in enumerate(source,1):  print linenum, line

Así que aparentemente start se guardó porque era muy valioso, y stop se eliminó porque tenía menos casos de uso y contribuyó a la confusión sobre el uso de la nueva función.

Evite rebanar con notación de subíndice

Otra respuesta dice:

¿Por qué no usar simplemente

for item in items[:limit]: # or limit+1, depends

Aquí hay algunas desventajas:

  • Solo funciona para iterables que aceptan cortes, por lo que es más limitado.
  • Si aceptan el corte, generalmente crea una nueva estructura de datos en la memoria, en lugar de iterar sobre la estructura de datos de referencia, por lo que desperdicia memoria (todos los objetos integrados hacen copias cuando se cortan, pero, por ejemplo, las matrices numpy hacen una vista cuando se cortan). ).
  • Los iterables no rebanables requerirían el otro tipo de manejo. Si cambia a un modelo de evaluación perezoso, también tendrá que cambiar el código con el corte.

Solo debe usar el corte con notación de subíndice cuando comprenda las limitaciones y si hace una copia o una vista.

Conclusión

Supongo que ahora que la comunidad de Python conoce el uso de enumerar, los costos de confusión se verían superados por el valor del argumento.

Hasta ese momento, puedes usar:

for index, element in zip(range(limit), items):
    ...

o

for index, item in enumerate(islice(items, limit)):
    ...

o, si no necesita el índice en absoluto:

for element in islice(items, 0, limit):
    ...

Y evite dividir con notación de subíndice, a menos que comprenda las limitaciones.

Puedes usar itertools.islice para esto. acepta start, stop y step argumentos, si está pasando solo un argumento, entonces se considera como stop. Y funcionará con cualquier iterable.

itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])

Manifestación:

>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
    print item,
...
0 1 2 3 4

Ejemplo de documentos:

islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G

¿Por qué no usar simplemente

for item in items[:limit]: # or limit+1, depends
    print(item)    # or whatever function of that item.

Esto solo funcionará para algunos iterables, pero dado que especificó Listas, funciona.

No funciona si usa conjuntos o dictados, etc.

Sección de Reseñas y Valoraciones

Recuerda difundir este enunciado si te fue de ayuda.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *