60 votes

manière pythonique d'itérer sur une partie d'une liste

Je veux itérer sur tout ce qui se trouve dans une liste sauf les premiers éléments, par exemple :

for line in lines[2:]:
    foo(line)

C'est concis, mais cela copie toute la liste, ce qui est inutile. Je pourrais le faire :

del lines[0:2]
for line in lines:
    foo(line)

Mais cela modifie la liste, ce qui n'est pas toujours bon.

Je peux le faire :

for i in xrange(2, len(lines)):
    line = lines[i]
    foo(line)

Mais, c'est juste dégoûtant.

Le mieux serait peut-être ça :

for i,line in enumerate(lines):
    if i < 2: continue
    foo(line)

Mais ce n'est pas aussi évident que le tout premier exemple.

Alors : Quel est le moyen de le faire qui soit aussi évident que le premier exemple, mais qui ne copie pas la liste inutilement ?

11 votes

Je pense for i in xrange(2, len(lines)): est bien.

0 votes

Juste par curiosité, quelle est la taille de la liste sur laquelle vous itérez ?

0 votes

@GregGuida : environ 300 lignes de texte.

47voto

soulcheck Points 17680

Vous pouvez essayer itertools.islice(iterable[, start], stop[, step]) :

import itertools
for line in itertools.islice(list , start, stop):
     foo(line)

2 votes

@NullUserException merci ! J'allais ajouter cela mais mon ordinateur a eu un problème.

1 votes

Ce qui serait génial, c'est quelque chose comme : for line in lazy(list)[:2]: foo(line) , donde lazy enveloppe l'itérable et, si vous le découpez, appelle islice à la place, etc. De cette façon, cela ressemble à une tranche (comme il se doit).

1 votes

@Claudiu comme les tranches sont censées être des objets du même type que la tranche, cela signifierait que lazy() crée un itérateur. C'était discuté sur la liste de diffusion python et Guido s'est opposé à cette idée. Cela n'empêche pas les implémentations indépendantes, mais vous ne le verrez pas de sitôt dans la bibliothèque standard.

11voto

ekhumoro Points 23190

Bien que itertools.islice semble être la solution optimale pour ce problème, mais d'une manière ou d'une autre, l'importation supplémentaire semble excessive pour quelque chose d'aussi simple.

Personnellement, je trouve que le enumerate solution parfaitement lisible et succincte - bien que je préférerais l'écrire comme ceci :

for index, line in enumerate(lines):
    if index >= 2:
        foo(line)

2 votes

Personnellement, je préfère avoir le moins d'indentations possible. le contrôle d'erreur + continue/return/break/etc en premier, puis le reste est en retrait

0 votes

@Claudiu : Et ce que vous dites permet également de faire passer l'intention immédiatement ; vous n'avez pas besoin de regarder à la fin de l'article. if pour voir si quelque chose se passe après lui ou dans un bloc else bloc.

1 votes

@ChrisMorgan. Plus, c'est moins. Dans l'exemple, une continue créerait une branche redondante dans le code, obligeant inutilement le lecteur à analyser deux blocs de code, plutôt qu'un seul. Si le code à l'intérieur de la boucle était plus complexe, alors continue pourraient certainement être utilisées pour améliorer la lisibilité en réduisant les retraits. Mais ce n'est pas ce que l'exemple tente d'illustrer.

5voto

Howard Points 23487

Vous pourriez construire un générateur d'aide :

def rangeit(lst, rng):
  for i in rng:
    yield lst[i]

for e in rangeit(["A","B","C","D","E","F"], range(2,4)):
  print(e)

0 votes

C'est bien. Mais c'est limité aux listes et ne fonctionne pas bien pour les itérables arbitraires.

1 votes

@S.Lott Vous avez raison bien sûr. Mais cela répond aux exigences de l'OP et c'est la première chose qui m'est venue à l'esprit - malheureusement je ne mémorise pas toutes les fonctions de itertools comme d'autres semblent le faire ;-)

4voto

philofinfinitejest Points 1646
for fooable in (line for i,line in enumerate(lines) if i >= 2):
    foo(fooable)

3voto

Eduardo Ivanec Points 6244

Je préfère utiliser abandonner pendant que pour ça. Cela semble naturel après l'avoir utilisé en Haskell et dans d'autres langages, et semble raisonnablement clair. Vous pouvez également l'utiliser dans de nombreux autres cas où vous souhaitez rechercher une condition "déclencheur" plus complexe que l'indice de l'élément pour le début de l'itération.

from itertools import dropwhile

for item in dropwhile(lambda x: x[0] < 2, enumerate(lst)):
  # ... do something with item

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X