Christopher Spears wrote: > How does this script work? > > #!/usr/bin/python > > class IteratorExample: > def __init__(self, s): > self.s = s > self.next = self._next().next > self.exhausted = 0 > def _next(self): > if not self.exhausted: > flag = 0 > for x in self.s: > if flag: > flag = 0 > yield x > else: > flag = 1 > self.exhausted = 1 > def __iter__(self): > return self._next() > > def main(): > a = IteratorExample('edcba') > for x in a: > print x > print '=' * 30 > a = IteratorExample('abcde') > print a.next() > print a.next() > print a.next() > print a.next() > print a.next() > print a.next() > > if __name__ == '__main__': > main() > > > Here is the output: > > d > b > ============================== > b > d > Traceback (most recent call last): > File "./python_101_iterator_class.py", line 35, in ? > main() > File "./python_101_iterator_class.py", line 29, in > main > print a.next() > StopIteration > > I think a lot of my confusion comes from not > understanding what _next is. I got this script from > an online tutorial at python.org. Is there a better > way to write the script, so I can actually understand it?
_next() is a generator - a function which, when called, returns an iterator. Each time the yield statement is reached, the iterator returns a new value. When the generator returns, the iteration ends. Generators are a very convenient way to package up iteration and state. Here is a simple example of a generator that counts to 2: In [1]: def count2(): ...: yield 1 ...: yield 2 ...: ...: You can iterate over the generator in a for loop: In [2]: for i in count2(): ...: print i ...: ...: 1 2 If you prefer you can explicitly call the next() method, which is a common method of all iterators: In [3]: c=count2() In [4]: c.next() Out[4]: 1 In [5]: c.next() Out[5]: 2 When the iterator is exhausted, it raises StopIteration. Again, this is standard behaviour for all iterators: In [6]: c.next() ------------------------------------------------------------ Traceback (most recent call last): File "<ipython console>", line 1, in ? StopIteration Your example seems like a complicated way to wrap an iterable in an iterator which returns every other element. Maybe I am missing something, but I would write it like this: In [7]: def skipper(seq): ...: it = iter(seq) # make sure we have an iterator ...: while True: ...: it.next() # skip a value ...: yield it.next() # return a value ...: ...: In [8]: for a in skipper('edcba'): ...: print a ...: ...: d b In [9]: a = skipper('edcba') In [10]: a.next() Out[10]: 'd' In [11]: a.next() Out[11]: 'b' In [12]: a.next() ------------------------------------------------------------ Traceback (most recent call last): File "<ipython console>", line 1, in ? File "<ipython console>", line 5, in skipper StopIteration You can read more about the iterator protocol and generators here: http://www.python.org/doc/2.2.3/whatsnew/node4.html http://www.python.org/doc/2.2.3/whatsnew/node5.html Read the referenced PEPs for all the juicy details. Hmm, a little Googling finds the tutorial you mention here: http://www.rexx.com/~dkuhlman/python_101/python_101.html#SECTION004460000000000000000 IMO this is a very confused example. You can write class-based iterators and you can write generator-based iterators, but combining them both to achieve such a simple result makes no sense to me. Kent _______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor