What makes an iterator an iterator?

7stud bbxx789_05ss at yahoo.com
Thu Apr 19 07:45:15 CEST 2007


Hi,

Thanks for the responses.

> 7stud <bbxx789_0... at yahoo.com> wrote:
> > Can you explain some of the details of why this code fails:
> ---
> class Parrot(object):
>     def __iter__(self):
>         return self
>     def __init__(self):
>         self.next = self.next().next
>     def next(self):
>         for word in "Norwegian Blue's have beautiful
> plumage!".split():
>             yield word
>
> P = Parrot()
> for word in P:
>     print word
> ------

On Apr 18, 8:45 pm, a... at mac.com (Alex Martelli) wrote:
>
> ...a loop like "for x in y:" binds an unnamed temporary
> variable (say _t) to iter(y) and then repeatedly calls _t.next() [or to
> be pedantic type(_t).next(t)] until that raises StopIteration.


Aiiii.  Isn't this the crux:

> repeatedly calls....[type(_t).next(t)]

 As far as I can tell, if the call was actually _t.next(), the code I
asked about would work.  However, the name look up for 'next' when you
call:

P.next()

is entirely different from the name look up for 'next' when you call:

type(P).next().

In the first lookup, the instance attribute "next" hides the class
attribute "next".  In the second lookup, the "next" attribute of the
class is returned, i.e. the next() method.  Yet, a generator-function-
call returns an iterator object that wraps the generator function, so
when the for loop makes repeated calls to type(P).next(), an iterator
object is repeatedly returned--what you want is i.next() to be
returned.

I suspected next() might be called through the class, but after
carefully parsing the section on iterators (Python in a Nutshell, p.
65) where it says iter() returns an object i, and then the for loop
repeatedly calls i.next(), I dismissed that idea.  I have to admit I'm
still a little confused by why you only parenthetically noted that
information and called it pedantic.


On Apr 18, 8:38 pm, "Terry Reedy" <tjre... at udel.edu> wrote:
>
> One very good way to get an iterator from an iterable is for .__iter__ to
> be a generator function.

Ahhh.  That eliminates having to deal with next().next constructs.
Nice.

> snip all examples of bad code that violate the iterator rule
> by improperly writing .next as a generator function

What iterator rule states that .next can't be a generator function?
My book says an iterator is any object with a .next method that is
callable without arguments (Python in a Nutshell(p.65) says the same
thing).   Also, my boos says an iterable is any object with an
__iter__ method.    As a result,  next() and __iter__() don't have to
be in the same object:

lass MyIterator(object):
    def __init__(self, obj):
        self.toIterateOver = obj
    def next(self):
        for x in range(self.toIterateOver.age):
            print x
        raise StopIteration

class Dog(object):
    def __init__(self, age):
        self.age = age
    def __iter__(self):
        return MyIterator(self)

d = Dog(5)
for year in d:
    print year


I've read recommendations that an iterator should additionally contain
an __iter__() method, but I'm not sure why that is.  In particular PEP
234 says:

----------
Classes can define how they are iterated over by defining an
    __iter__() method; this should take no additional arguments and
    return a valid iterator object.  A class that wants to be an
    iterator should implement two methods: a next() method that
behaves
    as described above, and an __iter__() method that returns self.

    The two methods correspond to two distinct protocols:

    1. An object can be iterated over with "for" if it implements
       __iter__() or __getitem__().

    2. An object can function as an iterator if it implements next().

    Container-like objects usually support protocol 1.  Iterators are
    currently required to support both protocols.  The semantics of
    iteration come only from protocol 2; protocol 1 is present to make
    iterators behave like sequences; in particular so that code
    receiving an iterator can use a for-loop over the iterator.

Classes can define how they are iterated over by defining an
    __iter__() method; this should take no additional arguments and
    return a valid iterator object.  A class that wants to be an
    iterator should implement two methods: a next() method that
behaves
    as described above, and an __iter__() method that returns self.

    The two methods correspond to two distinct protocols:

    1. An object can be iterated over with "for" if it implements
       __iter__() or __getitem__().

    2. An object can function as an iterator if it implements next().

    Container-like objects usually support protocol 1.  Iterators are
    currently required to support both protocols.  The semantics of
    iteration come only from protocol 2; protocol 1 is present to make
    iterators behave like sequences; in particular so that code
    receiving an iterator can use a for-loop over the iterator.
--------


>The semantics of
>    iteration come only from protocol 2

My example demonstrates that.

>protocol 1 is present to make
>    iterators behave like sequences; in particular so that code
>    receiving an iterator can use a for-loop over the iterator.

I don't understand that part--it looks like my example is using a for
loop over the iterator.




More information about the Python-list mailing list