2
from sortedcontainers import SortedSet

class BigSet(object):
    def __init__(self):
        self.set = SortedSet()
        self.current_idx = -1

    def __getitem__(self, index):
        try:
            return self.set[index]
        except IndexError as e:
            print('Exception: Index={0} len={1}'.format(index, len(self.ord_set)))
            raise StopIteration

    def add(self, element):
        self.set.add(element)

    def __len__(self):
        return len(self.set)

    def __iter__(self):
        self.current_idx = -1
        return self

    def __next__(self):
        self.current_idx += 1
        if self.current_idx == len(self.set):
            raise StopIteration
        else:
            return self.set[self.current_idx]

def main():
    big = BigSet()
    big.add(1)
    big.add(2)
    big.add(3)

    for b in big:
        print(b)

    for b2 in big:
        print(b2)

if __name__ == "__main__":
    main()

I have a class that embeds an iterable member variable named self.set and I would like to enable this class to support for loop. The above is the code that I wrote for the purpose. However, I think there must be a better way to do this task easier since the class has an iterable member already.

Question> Is there a way that I can delegate the job to the embedded self.set? Also, I think there maybe a good way to implement the __getitem__ too.

Thank you

q0987
  • 34,938
  • 69
  • 242
  • 387

1 Answers1

5

I am almost certain you do not want your container to be an iterator. So, it should not implement __next__. Instead, it should be iterable, so it only needs to implement __iter__. In that case, if you want to delegate to the iterable member:

def __iter__(self):
    return iter(self.set)

And remove your __next__ method.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • What is the difference between iterator and iterable? – q0987 Aug 02 '17 at 17:59
  • 1
    @q0987 An iterable is anything that implements `__iter__`. That method should *return an iterator*. An iterator implements `__iter__` (usually returning just itself) **and** `__next__`. Interestingly, there was a [question last night](https://stackoverflow.com/questions/45458631/how-python-built-in-function-iter-convert-a-python-list-to-an-iterator/45458810#45458810) that fundamentally had the same confusion. Check out my answer there, and also [this classic question](https://stackoverflow.com/questions/16301253/what-exactly-is-pythons-iterator-protocol) – juanpa.arrivillaga Aug 02 '17 at 18:02
  • @q0987 and for good measure, [the docs](https://docs.python.org/3.6/library/stdtypes.html#iterator-types) – juanpa.arrivillaga Aug 02 '17 at 18:02
  • May you also please help check the definition of __getitem__. I think there must be a better way. Thank you – q0987 Aug 02 '17 at 18:05
  • @q0987 also, [this blog post](http://nvie.com/posts/iterators-vs-generators/) tries to explain the difference between containers, iterables, iterators, and generators. These things are often conflated, but despite being related, they are all distinct. One of my biggest pet peeves is when people conflate *generators* with *iterators*. Generators are a language construct that allow for the easy writing of iterators! (and much more, like co-routines). They are not synonymous, though, as many people tend to use the terms. – juanpa.arrivillaga Aug 02 '17 at 18:05
  • @q0987 why is `__getitem__` raising `StopIteration`? You should just raise `IndexError`. So again, you can simply delegate to `return self.set.__getitem__(index)`, perhaps capturing the `IndexError` and raising *another* `IndexError` with a custom message. But definitely not `StopIteration` – juanpa.arrivillaga Aug 02 '17 at 18:07
  • Sorry the StopIteration is a typo. Thank you! – q0987 Aug 02 '17 at 18:13