Ian Kelly wrote: > On Mon, Feb 9, 2015 at 4:30 PM, Steven D'Aprano > <steve+comp.lang.pyt...@pearwood.info> wrote: [...] >> Your class is itself an iterator. > > This is an anti-pattern, so don't even suggest it. Iterables should > never be their own iterators. Otherwise, your iterable can only be > iterated over once!
Hmmm, good point. However, I will point out a couple of factors: Ultimately, that's the correct behaviour for *iterator* classes. With the rich set of tools available to build iterators from built-in parts, it is rare that you need to write your own class with a __next__ method, but if you do, that's the way you want it to behave. Whether that *iterator* class should be the same class as the *iterable* class is another story. In the built-ins, they mostly (always?) come in pairs: tuple <-> tuple_iterator list <-> list_iterator dict <-> dict_keyiterator set <-> set_iterator range <-> range_iterator so that's an excellent sign that doing so is best practice, but it should not be seen as *required*. After all, perhaps you have good reason for wanting your iterable class to only be iterated over once. Also, *technically* iterators may be re-iterable. The docs say that iterators which fail to raise StopIteration forever once they are exhausted are "broken", but the docs do not forbid broken iterators. Consenting adults and all that. You might want an iterator with a reset() method. Even an outright broken iterator! def __next__(self): if random.random() < 0.1: raise StopIteration return random.random() Why you would want one, I don't know, but if you have a hankering for such a beast, Python lets you do it. > The proper version of the "hard way" is: > > 1) The __iter__ method of the iterable constructs a new iterator > instance and returns it. > > 2) The __iter__ method of the *iterator* simply returns itself. > > 3) The __next__ method of the iterator tracks the current value and > returns the next value. Note that the iterator should never store the > iterable's data internally, unless the iterable is immutable and the > calculation is trivial (e.g. a range object). Instead, it should > determine the next value by referring to its source iterable. -- Steven -- https://mail.python.org/mailman/listinfo/python-list