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_longest
que 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.