2
\$\begingroup\$

I'm trying to implement the enumerate built-in using iterators. So far it works but for unordered objects, such as sets or dictionaries, I'm not sure if this implementation is considered valid, although it works.

class myEnumerate:
    def __init__(self, iterable):
        self.iterable = list(iterable)
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index >= len(self.iterable):
            raise StopIteration
        return self.index, self.iterable.__getitem__(self.index)


if __name__ == '__main__':
    for (i, ch) in myEnumerate("Python"):
        print(i, ch)

    # Output:
    # 0 P
    # 1 y
    # 2 t
    # 3 h
    # 4 o
    # 5 n

    for (i, ch) in myEnumerate(set(["a", "b", "c"])):
        print(i, ch)

    # Output: (order might differ)
    # 0 a
    # 1 b
    # 2 c

What do you guys think?

\$\endgroup\$
3
\$\begingroup\$

I think this is mistake:

self.iterable = list(iterable)
  1. It requires loading the entire iterable into memory. Being able to avoid this and work with large collections efficiently is one of the big benefits of iterators.

  2. It won't work on non-ending iterators like itertools.count()

If instead, you make an iterator from the iterable, you can avoid both those problems and simplify your code by depending on the iterator's own StopIteration exception:

class myEnumerate:
    def __init__(self, iterable):
        self.iterable = iter(iterable)
        self.index = -1

    def __iter__(self):
        return self

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

// this now works:

from itertools import count, islice
c = count()
for (i, ch) in islice(myEnumerate(count()), 1, 10):
    print(i, ch)
\$\endgroup\$
  • 1
    \$\begingroup\$ You're absolutely right! Thx a lot. \$\endgroup\$ – Maxwell's Daemon Jul 12 at 19:17
0
\$\begingroup\$

As mentioned, expanding the iterable into a list is probably not always desired. Also a class seems to be overkill, since it needs to carry around an instance dict that's not being used. An implementation as a generator function seems more lightweight and feasible.

def enumerate(iterable, start=0):
    for item in iterable:
        yield (start, item)
        start += 1
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.