4

I want to step the loop iterator explicitly inside the loop. Is there a 'nicer' way to do this than:

idx = iter(range(0, 10))

for i in idx:
   print i

   if i == 5:
      print "consuming %i in step %i" % (next(idx), i)

Edit: I wander if there is a way to get access to the loop-iterator other than defining it explicitly as in my example.

Thanks!

MrPugh
  • 43
  • 1
  • 5
  • 3
    What's wrong with `next(idx)`? Isn't it explicit and nice enough? – eumiro Feb 13 '11 at 08:22
  • @eumiro: Not if `idx` isn't already an iterator (eg. a list). It's also very uncommon practice to take the `next` of an iterator while you're iterating over it. I'd strongly recommend against it. – Glenn Maynard Feb 13 '11 at 09:26
  • I am concerned about the head of the loop; usually one would see something like `for i in range(0, 10)` - but then I don't get access to the iterator-object... – MrPugh Feb 13 '11 at 13:34
  • Related: https://stackoverflow.com/questions/4810160/iterate-again-within-the-for-loop – Denilson Sá Maia Oct 06 '15 at 12:36
  • also related, and with a more thorough explanation on why this works: https://stackoverflow.com/a/46522452/6053327 – Ilja Oct 02 '17 at 09:52

2 Answers2

3
data = list(range(10))

it = iter(data)
for i in it:
   if i==5:
       j = it.next()
       print "Consuming {0},{1}".format(i,j)
   else:
       print i

results in

0
1
2
3
4
Consuming 5,6
7
8
9
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
  • 1
    I am concerned about the head of the loop. Is the iterator of a for-loop exposed in some way - or do I have to state the iterator explicitly as in my (and your) example to get access to it? I see that asking for a 'nicer' way in general was not very clearly... – MrPugh Feb 14 '11 at 03:35
  • So far as I am aware there is no way to directly access the loop iterator; you need to explicitly create it and keep a copy beforehand. – Hugh Bothwell Feb 14 '11 at 18:13
  • +1 for a good point: you can steal further items from your iterator, even if you are inside the cycle. NOTE: you may get a StopIteration which terminates the loop apparently clean way, but this may put you into an inconsistent state with your program logic. – Dacav Jul 19 '13 at 07:58
0

You could define a generator to yield elements from the iterator singly or in pairs. This keeps the for-loop nice and simple, while isolating the filtering logic in the generator.

def my_filter(iterable):
    result=[]
    for i in iterable:
        result.append(i)
        if i==5:
            continue
        yield result
        result=[]        

idx = iter(range(0, 10))

for i in my_filter(idx):
    print i
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • I want to consume all elements; in some cases I just need to consume them in the same iteration... Sometimes the elements I am iterating need to be consumed in pairs, usually they are treated as 'single'. – MrPugh Feb 13 '11 at 13:33
  • @MrPugh: You can modify the generator (`my_filter`), so that it yields an object that contains either a single element from `idx` or multiple elements from `idx`. Ideally, the `for-loop` should process those objects homogenously -- it shouldn't care if the object contains a single element from `idx` or multiple elements. – unutbu Feb 13 '11 at 14:01
  • I've modified the code to show what I mean. Above, I just use a list to collect the single, or paired elements. Depending on your problem, you may want to use some other container, such as a named tuple, or dict, or maybe a custom class... – unutbu Feb 13 '11 at 14:06
  • I like that `my_filter` would generate my pairings; however my actual code is a bit more complicated - and the `continue` in the `my_filter`-loop would not work nicely. So it turns me back to the original question: how can I consume the next element within the same iteration... Maybe creating the iterator-object explicitly and using `next()` as in my initial example is the way to go for me... Thanks for you effort! – MrPugh Feb 13 '11 at 14:23