[Tutor] _next

Kent Johnson kent37 at tds.net
Thu May 25 04:31:32 CEST 2006


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



More information about the Tutor mailing list