terminar - for i in range python español
¿Cómo puedo limitar las iteraciones de un bucle en Python? (5)
Digamos que tengo una lista de elementos, y quiero iterar sobre los primeros:
items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5
Implementación ingenua
La naïf de Python que proviene de otros idiomas probablemente escribiría este código perfectamente útil y ejecutable (si es unidiomático):
index = 0
for item in items: # Python''s `for` loop is a for-each.
print(item) # or whatever function of that item.
index += 1
if index == limit:
break
Implementación más idiomática.
Pero Python tiene enumeración, que abarca aproximadamente la mitad de ese código:
for index, item in enumerate(items):
print(item)
if index == limit: # There''s gotta be a better way.
break
Así que hemos reducido el código extra a la mitad. Pero tiene que haber una mejor manera.
¿Podemos aproximarnos al siguiente comportamiento de pseudocódigo?
Si enumerate tomó otro argumento de stop
opcional (por ejemplo, toma un argumento de start
como este: enumerate(items, start=1)
), creo que sería ideal, pero el siguiente no existe (consulte la documentación en enumerar aquí ):
# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
print(item)
Tenga en cuenta que no habría necesidad de nombrar el index
porque no hay necesidad de referenciarlo.
¿Hay una forma idiomática de escribir lo anterior? ¿Cómo?
Una pregunta secundaria: ¿por qué no se incluye esto enumerar?
¿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
detiene en el más corto iterable de sus argumentos. (En contraste con el comportamiento de zip_longest
, que usa el iterable más largo).
range
puede proporcionar una iteración limitada que podemos pasar a zip junto con nuestra iterable primaria.
Así que podemos pasar un objeto de range
(con su argumento de stop
) a zip
y usarlo como una enumeración limitada.
zip(range(limit), items)
Usando Python 3, zip
y range
devuelve iterables, que canalizan los datos en lugar de materializar los datos 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
e 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)
Lo que no requiere ser asignado al índice.
Componer enumerate(islice(items, stop))
para obtener el índice
Como lo señala Pablo Ruiz Ruiz, también podemos componer las imágenes con números enumerar.
for index, item in enumerate(islice(items, limit)):
print(index, item)
¿Por qué no está incorporado en
enumerate
?
Aquí se enumera implementado 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 sería menos eficaz para aquellos que ya usan enumerar, ya que tendría que verificar si es hora de detener cada iteración. Solo podemos verificar y usar el antiguo enumerado 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)
Este control adicional tendría un impacto insignificante en el rendimiento.
En cuanto a por qué enumerar no tiene un argumento de detención, esto se propuso originalmente (ver PEP 279 ):
Esta función se propuso originalmente con argumentos opcionales de inicio y detención. GvR [Guido van Rossum] señaló que la función de
enumerate(seqn, 4, 6)
llamadaenumerate(seqn, 4, 6)
tenía una interpretación alternativa plausible como una porción que devolvería los elementos cuarto y quinto de la secuencia. Para evitar la ambigüedad, los argumentos opcionales se eliminaron a pesar de que significaba perder flexibilidad como contador de bucles. Esa flexibilidad fue lo más importante para el caso común de contar desde uno, como en:
for linenum, line in enumerate(source,1): print linenum, line
Aparentemente, el start
se mantuvo porque era muy valioso, y se stop
porque tenía menos casos de uso y contribuyó a la confusión en el uso de la nueva función.
Evitar rebanar con notación de subíndices.
Otra respuesta dice:
¿Por qué no simplemente usar
for item in items[:limit]: # or limit+1, depends
Aquí hay algunos inconvenientes:
- Solo funciona para iterables que aceptan rebanar, 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 tanto, desperdicia memoria (todos los objetos incorporados hacen copias cuando se cortan, pero, por ejemplo, las matrices numpy hacen una vista cuando se cortan ).
- Los iterables que no se pueden cortar requerirían otro tipo de manejo. Si cambia a un modelo de evaluación perezoso, también tendrá que cambiar el código con la segmentación.
Solo debe usar la segmentación con notación de subíndices cuando comprende las limitaciones y si hace una copia o una vista.
Conclusión
Supongo que ahora la comunidad de Python conoce el uso de enumerar, los costos de confusión serí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 necesitas el índice:
for element in islice(items, 0, limit):
...
Y evite cortar con notación de subíndice, a menos que entienda las limitaciones.
¿Por qué no hacer un bucle hasta el límite o el final de la lista, lo que ocurra antes, como esto:
items = range(10)
limit = 5
for i in range(min(limit, len(items))):
print items[i]
Salida:
0
1
2
3
4
¿Por qué no simplemente usar
for item in items[:limit]: # or limit+1, depends
print(item) # or whatever function of that item.
Esto solo funcionará para algunos iterables, pero como especificó Listas, funciona.
No funciona si usas Sets o Dicts, etc.
Pase islice con el límite dentro de enumerar.
a = [2,3,4,2,1,4]
for a, v in enumerate(islice(a, 3)):
print(a, v)
Salida:
0 2
1 3
2 4
Puedes usar itertools.islice
para esto. Acepta los argumentos de start
, stop
y step
, 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