1

Consider the following program, from the title which should be self explanatory . I need to implement the function next_index(it) that returns the index of next item an iterator is about to return.

def next_index(it):
    #do something here
    #return the index of next element the iterator will fetch
    return -1 #dummy value being returned now

l = [0,1,2,3,4]
it = iter(l)
fst = it.__next__()
print(fst) # prints 0, the next call to it.__next__() will return the element at index 1

n_i = next_index(it)
#n_i should be 1
print(n_i)

_ = it.__next__()

n_i = next_index(it)
#n_i should be 2 now
print(n_i)

I know that iterators are typically used when you do not need the index and for index we can use enumerate. However, I am trying to do some dynamic analysis with bytecode level tracing. Loops like the following are iterated using iterators. I need to track the index being accessed by the iterator. Though there should be workarounds, e.g., keeping track of the index in the analysis prgroam explicitely, a function like next_index(it) would make it easy and less error-prone.

l = [0,1,2,3,4]
for c in l:
    print(c)
Mr. Nobody
  • 185
  • 11
  • 2
    The iterator itself does not expose this. Other than hacking the CPython internals, your best option is just to keep track of the index yourself. – juanpa.arrivillaga Apr 11 '19 at 02:20
  • Why not just add one to the result from `enumerate`? – nicholishen Apr 11 '19 at 02:25
  • @juanpa.arrivillaga - It would have been great if there was any such functionality, at least for my purposes. Thanks anyways. If you are sure and add some reference if possible, why don't you add this as an answer? – Mr. Nobody Apr 11 '19 at 02:30
  • @nicholishen - I cannot do that, as I am analyzing the bytecode generated from a program. And I cannot change the way (using iterator) bytecode the bytecode is generated. – Mr. Nobody Apr 11 '19 at 02:30
  • @Mr.Nobody how do you even know *that there is any index involved though*? In general, iteterators do no work with indices. I believe some of the built-in sequence types do, e.g. list, str, tuple, but others will use something else, e.g. `set`, or at least, won't know in advance what "index' is next, and the index won't be meaningful to you, or others won't use an index at all, (generators, many custom things etc) – juanpa.arrivillaga Apr 11 '19 at 02:32
  • @juanpa.arrivillaga I think he just means the index of the resulting sequence of values, not an index for the underlying iterable. – chepner Apr 11 '19 at 03:26
  • @Mr.Nobody You say that you are tracing the byte code, but where would you use `next_index` in the first place? If you are inserting calls to `next_index` in the code, you could also insert a wrapper around the call to `iter` to return something that acts just like a regular iterator, but includes a hook for enumerating the things it yields. – chepner Apr 11 '19 at 03:30

1 Answers1

0

Wrap the iterator with something that keeps count of what's been yielded.

class EnumeratedIter:
    def __init__(self, it):
        self.it = it
        self.index = 0

    def __next__(self):
        self.index += 1
        return next(self.it)

    def __iter__(self):
        return self


def next_index(it):
    return it.index

l = list("abcde")
it = EnumeratedIter(iter(l))

n_i = next_index(it)
assert n_i == 0

next(it)
n_i = next_index(it)
assert n_i == 1

next(it)
n_i = next_index(it)
assert n_i == 2
chepner
  • 497,756
  • 71
  • 530
  • 681