web-dev-qa-db-fra.com

python incrémentant arbitrairement un itérateur dans une boucle

J'y vais probablement de la mauvaise manière, mais je me demandais comment gérer cela en python. 

D'abord du 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 en python, je n'ai qu'une solution, introduire un compteur et une autre instruction if. rompre la boucle jusqu'à ce que le compteur atteigne 5 après qu'exp.match (ligne) est vraie. 

Il doit exister un meilleur moyen de le faire, espérons-le, qui ne nécessite pas l'importation d'un autre module. 

Merci d'avance! 

46
jr0d

Il existe un paquet fantastique en Python appelé itertools .

Mais avant d’entrer dans ces termes, il serait utile 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 la méthode __iter__() class qui fournit un type iterator . "Comprendre l'instruction 'pour' de Python" est un article de Nice qui décrit le fonctionnement de l'instruction for-in en Python et fournit une vue d'ensemble de Nice sur le fonctionnement des types d'itérateurs.

Jetez un oeil à 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

Revenons maintenant à itertools. Le paquet contient des fonctions à différentes fins d'itération. Si vous avez besoin de faire un séquençage spécial, c'est le premier endroit à examiner.

En bas, vous trouverez la section Recipes qui contient recettes pour créer un jeu d’outils étendu en utilisant les outils de navigation existants comme blocs de construction.

Et il y a 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 sur 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
43
Filip Dupanović

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:


 avant de passer 
 5 après de passer 
 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 devez effectuer de très grands sauts tels que i + = 100000000, vous devez utiliser une boucle explicite while au lieu de simplement sauter des étapes d'une boucle for.

Voici l'exemple qui utilise la boucle explicite while à la place de 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 l'exemple islice ci-dessus.

16
jfs

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

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

Faire avancer un itérateur est un peu plus difficile cependant. Si vous ne souhaitez pas utiliser la méthode du compteur, vous pouvez probablement créer votre liste au préalable, probablement en scindant cdata, puis en travaillant sur 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 prétendez ê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
2
Benno

Je ne suis pas tout à fait 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

Vous ne savez pas vraiment ce que vous cherchez, mais la fourchette (len (..)) devrait vous aider.

1
rh0dium

Vous pouvez supprimer des valeurs d'un itérateur

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

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

1
u0b34a0f6ae

pour votre exemple, puisque vous travaillez avec des listes (séquences indexables) et non avec des itérateurs, je vous recommande ce qui suit:

lines = cdata.split("\n")
for line in lines[:50]+lines[60:]:
  print line

ce n'est pas le plus efficace car il construit potentiellement 3 nouvelles listes (mais si la partie ignorée est plus grande que la partie traitée, elle pourrait être plus efficace que les autres options), mais elle est assez claire et explicite.

Si vous ne voulez pas utiliser le module itertools, vous pouvez facilement convertir les listes en séquences:

from itertools import chain, islice
for line in chain(islice(lines, None, 50), islice(lines, 60,None)):
  print line
0
fortran

Peut-être avec genexps. Pas joli mais ...

Quelque chose comme ca:

>>> gx = (line for line in '1 2 x 3 4 5 6 7 x 9 10 11 12 x 1'.split('\n'))
>>> for line in gx:
...   if line == 'x':
...      for i in range(2):
...          line = gx.next()
...   print line

Le seul problème est de s'assurer que gx peut être next () - ed. L'exemple ci-dessus génère volontairement une exception en raison du dernier x.

0
mjv