Comment puis-je limiter le nombre d'itérations d'une boucle en Python?
Dire que j'ai une liste d'éléments, et je veux effectuer une itération sur les premiers de celui-ci:
items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5
Implémentation naïve
Le Python naïf venant d'autres langues serait probablement écrire parfaitement fonctionnel et performant (si unidiomatic) code:
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
Plus idiomatique de la mise en œuvre
Mais Python a énumérer, qui regroupe environ la moitié de ce code bien:
for index, item in enumerate(items):
print(item)
if index == limit: # There's gotta be a better way.
break
Nous avons donc réduire le code supplémentaire dans la moitié. Mais il doit bien y avoir une meilleure façon.
Peut-on rapprocher le pseudo-code ci-dessous comportement?
Si énumérer pris une autre option stop
argument (par exemple, il faut un start
argument comme ceci: enumerate(items, start=1)
) qui, je pense, être idéal, mais le ci-dessous n'existe pas (voir la documentation sur les énumérer ici):
# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
print(item)
Noter qu'il n'y aurait pas besoin de nom de la index
car il n'est pas besoin de faire référence à elle.
Est-il un idiomatiques façon d'écrire ci-dessus? Comment?
Une question secondaire: pourquoi n'est-ce pas construit en énumérer?
Vous devez vous connecter pour publier un commentaire.
Y compris l'indice
zip
s'arrête sur la plus courte de itératif de ses arguments. (En contraste avec le comportement dezip_longest
, qui utilise la plus longue itératif.)range
peut fournir un limitées itératif que nous pouvons passer à zip avec notre principal objet iterable.Afin que nous puissions passer un
range
objet (avec sesstop
argument) pourzip
et l'utiliser comme un limitées énumérer.zip(range(limit), items)
À l'aide de Python 3,
zip
etrange
retour iterables, qui pipeline de données au lieu de matérialiser les données dans les listes pour les étapes intermédiaires.Pour obtenir le même comportement en Python 2, en remplaçant simplement l'
xrange
pourrange
etitertools.izip
pourzip
.Si ne nécessitant pas l'indice,
itertools.islice
Vous pouvez utiliser
itertools.islice
:qui ne nécessite pas d'assigner à l'index.
La composition
enumerate(islice(items, stop))
pour obtenir l'indexComme Pablo Ruiz Ruiz points, nous pouvons également composer islice avec énumérer.
Ici la liste de mises en œuvre en pur Python (avec des modifications possibles pour obtenir le comportement souhaité dans les commentaires):
Ci-dessus serait moins performant pour ceux qui utilisent les énumérer déjà, parce qu'il aurait pour vérifier si il est temps de s'arrêter à chaque itération. Il nous suffit de vérifier et utiliser l'ancien énumérer si vous n'obtenez pas un arrêt argument:
Cette vérification supplémentaire aurait une légère négligeable l'impact sur les performances.
À pourquoi énumérer ne dispose pas d'un arrêt d'argument, il a été proposé à l'origine (voir PEP 279):
Donc, apparemment,
start
a été maintenu, car il a été très précieux, etstop
a été abandonné car il a eu moins de cas d'utilisation et contribué à la confusion sur l'utilisation de la nouvelle fonction.Éviter de trancher avec indice de notation
Une autre réponse dit:
Voici quelques-uns:
Vous devez uniquement utiliser le découpage indice de notation lorsque vous comprendre les limites et s'il rend une copie ou d'un point de vue.
Conclusion
Je présume que maintenant la communauté Python sait l'utilisation d'énumérer, la confusion des coûts serait compensé par la valeur de l'argument.
Jusqu'à ce que le temps, vous pouvez utiliser:
ou
ou, si vous n'avez pas besoin de l'indice à tous:
Et d'éviter de trancher avec indice de notation, à moins de comprendre les limites.
Vous pouvez utiliser
itertools.islice
pour cela. Il acceptestart
,stop
etstep
arguments, si vous êtes de passage seulement un argument, alors il est considéré commestop
. Et il fonctionne avec n'importe quel itérable.Démo:
Exemple de docs:
islice(generator, None)
pour obtenir toutes les itérations - ce qui est utile si vous souhaitez limiter les itérations en fonction des conditionsPourquoi ne pas simplement utiliser
Cela ne fonctionne que pour certains iterables, mais depuis que vous avez spécifié les Listes, il fonctionne.
Il ne fonctionne pas si vous utilisez des Ensembles ou des dicts etc.
Passer islice avec la limite à l'intérieur d'énumérer
De sortie:
Pourquoi ne pas en boucle jusqu'à la limite ou la fin de la liste, selon la première éventualité, comme ceci:
De sortie: