Bas Jansen
Bas Jansen

Reputation: 3343

Which element are we currently at in a "for x in y:" loop

The python syntax of for x in y: to iterate over a list must somehow remember what element the pointer is at currently right? How would we access that value as I am trying to solve this without resorting to for index, x in enumerate(y):

The technical reason why I want to do this is that I assume that enumerate() costs performance while somehow accessing the 'pointer' which is already existing would not. I see that from the answers however this pointer is quite private and inaccessible however.

The functional reason why I wanted to do this is to be able to skip for instance 100 elements if the current element float value is far off from the 'desired' float range.

-- Answer --

The way this was solved was as follows (pure schematic example):

# foo is assumed to be ordered in this example
foo = [1,2,3,4,5,6....,99,100]
low = 60
high = 70
z = iter(foo)
for x in z:
    if x < low-10
        next(islice(z,10,10),None)
    if x > high
        break

Upvotes: 0

Views: 252

Answers (4)

Stephane Rolland
Stephane Rolland

Reputation: 39926

No, it uses the underlying iterator, which is not forced to keep track of a current index.

Unless you manually incerement a counter, this is not possible:

idx = 0
for x in y:
    idx+=1
    # ...

so, just keep with enumerate()

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1124100

You cannot. for uses the Python iteration protocol, which for lists means it'll create a private iterator object. That object keeps track of the position in the list.

Even if you were to create the iterator explicitly with iter(), the current position is not a public property on that object:

>>> list_iter = iter([])
>>> list_iter
<listiterator object at 0x10056a990>
>>> dir(list_iter)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__length_hint__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'next']

The point of the iteration protocol is that it lets you treat any sequence, even one that continues without bounds, in exactly the same way. It is trivial to create a sequence generator that does not have a position:

def toggle():
    while True:
        yield True
        yield False

That generator will toggle between True and False values as you iterate over it. There is no position in a sequence there, so there is no point exposing a position either.

Just stick to enumerate(). All that enumerate() has to do is keep a counter. It doesn't keep position in the wrapped iterator at all. Incrementing that integer does not cost you much performance or memory.

enumerate() is basically this, implemented in C:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

Because it is implemented in C, it'll beat trying to read an attribute on the original iterator any day, which would require more bytecode operations in each iteration.

Upvotes: 7

Michael Wild
Michael Wild

Reputation: 26381

This information is internal to the iterator and cannot be accessed. See here for a description of the iterator protocol. Essentially, the only publicly available member of the iterator is next() which raises a StopIteration exception once the range is exhausted.

Besides, enumerate is pretty efficient. It is the equivalent of writing

i = -1
for x in y:
  i += 1
  # do something with x and i

Upvotes: 1

akaIDIOT
akaIDIOT

Reputation: 9231

That 'pointer' value is internal to whatever it is that created the iterator. Remember that is doesn't need to be a list (something that can be indexed), so if you really want the 'index', you will need to resort to using enumerate.

Upvotes: 1

Related Questions