49 votes

python incrémente arbitrairement un itérateur à l'intérieur d'une boucle

Je m'y prends probablement de la mauvaise manière, mais je me demandais comment gérer cela en Python.

D'abord un peu de code c :

int i;

for(i=0;i<100;i++){
  if(i == 50)
    i = i + 10;
  printf("%i\n", i);
}

Ok, donc on ne voit jamais les années 50...

Ma question est la suivante : comment puis-je faire quelque chose de similaire en python ? Par exemple :

for line in cdata.split('\n'):
  if exp.match(line):
    #increment the position of the iterator by 5?
    pass
  print line

Avec mon expérience limitée de Python, je n'ai qu'une solution, introduire un compteur et une autre instruction if. Interrompre la boucle jusqu'à ce que le compteur atteigne 5 après que exp.match(line) soit vrai.

Il doit y avoir un meilleur moyen de le faire, en espérant qu'il n'implique pas l'importation d'un autre module.

Merci d'avance !

44voto

Filip Dupanović Points 10071

Il existe un paquet fantastique en Python appelé itertools .

Mais avant d'entrer dans le vif du sujet, il serait bon d'expliquer comment le protocole d'itération est implémenté en Python. Lorsque vous souhaitez fournir une itération sur votre conteneur, vous spécifiez l'attribut __iter__() qui fournit une méthode de classe type d'itérateur . "Comprendre l'instruction 'for' de Python" est un bon article qui explique comment le for-in fonctionne réellement en Python et fournit un bon aperçu du fonctionnement des types d'itérateurs.

Jetez un coup d'œil à ce qui suit :

>>> sequence = [1, 2, 3, 4, 5]
>>> iterator = sequence.__iter__()
>>> iterator.next()
1
>>> iterator.next()
2
>>> for number in iterator:
    print number 
3
4
5

Maintenant, revenons à itertools . Le paquet contient des fonctions pour diverses itérations. Si vous avez un jour besoin de faire un séquençage spécial, c'est le premier endroit à consulter.

En bas, vous pouvez trouver le Recettes qui contiennent des recettes pour créer un ensemble d'outils étendu en utilisant les itertools existants comme blocs de construction .

Et il existe une fonction intéressante qui fait exactement ce dont vous avez besoin :

def consume(iterator, n):
    '''Advance the iterator n-steps ahead. If n is none, consume entirely.'''
    collections.deque(itertools.islice(iterator, n), maxlen=0)

Voici un exemple rapide et lisible de son fonctionnement (Python 2.5) :

>>> import itertools, collections
>>> def consume(iterator, n):
    collections.deque(itertools.islice(iterator, n))
>>> iterator = range(1, 16).__iter__()
>>> for number in iterator:
    if (number == 5):
        # Disregard 6, 7, 8, 9 (5 doesn't get printed just as well)
        consume(iterator, 4)
    else:
        print number

1
2
3
4
10
11
12
13
14
15

17voto

J.F. Sebastian Points 102961

itertools.islice :

lines = iter(cdata.splitlines())
for line in lines:
    if exp.match(line):
       #increment the position of the iterator by 5
       for _ in itertools.islice(lines, 4):
           pass
       continue # skip 1+4 lines
    print line

Par exemple, si exp , cdata sont :

exp = re.compile(r"skip5")
cdata = """
before skip
skip5
1 never see it
2 ditto
3 ..
4 ..
5 after skip
6 
"""

Alors la sortie est :

before skip
5 after skip
6 

Implémentation Python de l'exemple C

i = 0
while i < 100:
    if i == 50:
       i += 10
    print i
    i += 1

Comme @ [Glenn Maynard] l'a souligné dans le commentaire si vous avez besoin d'effectuer des sauts très importants tels que i += 100000000, vous devez utiliser la méthode explicite. while au lieu de simplement sauter des étapes dans une for boucle.

Voici l'exemple qui utilise l'explicitation while boucle à la place islice :

lines = cdata.splitlines()
i = 0
while i < len(lines):
    if exp.match(lines[i]):
       #increment the position of the iterator by 5
       i += 5
    else:
       print lines[i]
       i += 1

Cet exemple produit le même résultat que le précédent islice exemple.

2voto

Benno Points 3677

Si vous le faites avec des chiffres, la compréhension d'une liste peut fonctionner :

for i in [x for x in range(0, 99) if x < 50 and x > 59]:
    print i

Déplacer un itérateur vers l'avant est un peu plus difficile cependant. Je vous suggère d'établir votre liste à l'avance si vous ne voulez pas utiliser l'approche par compteur, probablement en divisant cdata, puis en calculant les index de la ligne correspondante et en supprimant cette ligne et les suivantes. En dehors de cela, vous êtes coincé avec l'approche du compteur qui n'est pas aussi désagréable que vous le dites pour être honnête.

Une autre option est la suivante :

iterator = iter(cdata.split('\n'))
for line in iterator:
    if exp.match(line):
        for i in range(0, 5):
            try:
                iterator.next()
            except StopIteration:
                break
    else:
        print line

1voto

rh0dium Points 2771

Je ne suis pas vraiment sûr de suivre votre processus de pensée, mais voici de quoi vous nourrir

for i in range(len(cdata.split('\n'))):
  if i in range(50,60): continue
  line = cdata[i]
  if exp.match(line):
    #increment the position of the iterator by 5?
    pass
  print line

Je ne suis pas sûr de ce que vous cherchez vraiment, mais le range(len(..)) devrait vous aider.

1voto

u0b34a0f6ae Points 14874

Vous pouvez déposer les valeurs d'un itérateur

def dropvalues(iterator, vals):
    for i in xrange(vals): iterator.next()

Maintenant, assurez-vous que vous avez un objet itérateur sur lequel travailler avec lines = iter(cdata.split('\n')) ; et bouclez dessus.

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