[Tutor] iterator question for a toy class

Kent Johnson kent37 at tds.net
Sat Apr 23 20:18:53 CEST 2005


Marcus Goldfish wrote:
> I see that an iterator is conceptually distinct from the container
> object it iterates over, but I am confused that both the iterator and
> container implement __iter__() to support the iterator protocol.

I think this is to simplify the Python runtime. 'for i in c' will work if c is an iterable container 
or an actual iterator. In either case the runtime can just call c.__iter__() and get an iterator.

   In
> my original Foo implementation, __iter__() returned a list, which
> supports the iterator protocol, so it "just worked" (albeit not for
> all cases, and not efficiently).  

Actually your Foo.__iter__() returned an iterator over a list. You called iter() on a list, which 
returns an iterator.

In general, though, how would I
> implement my own iterator (not using generator functions)?  Would I
> have to have a FooIterator class?  

Yes, that would be the simplest way.

What would FooIterator.__iter__()
> return?

self.

Here's an attempt at Foo.__iter__() that creates a custom iterator object...no, actually, it is so 
awkward I'm not going to bother. FooIterator has to keep a lot of state - the current list being 
indexed and the current index. Use a generator function!

>>make __iter__() into a generator function like
> 
> ... so gfs look much easier!  This is the first concrete use for gf's
> I've found in my code so far, and it rocks-- is it always possible to
> implement an iterator using gfs?  

Yes, at least there are no restriction I know of. Maybe some strange case where it doesn't make 
sense. The docs say, "Python's generators provide a convenient way to implement the iterator 
protocol. If a container object's __iter__() method is implemented as a generator, it will 
automatically return an iterator object (technically, a generator object) supplying the __iter__() 
and next() methods."

Is there a performance issue to be
> aware of when using gfs?

Not that I know of. Try it and see. It wouldn't surprise me to find that gfs are faster; generally 
the more you use builtin stuff the faster your code will run.

>>you want. A simple definition for __eq__() that finds these unequal would be
>>  def __eq__(self, other):
>>    return self.head == other.head and self.tail == other.tail
> 
> Ok, I like the modified __eq__(), but now I want my Foo class to store
> the head and tail lists as private attributes (self.__head,
> self.__tail).  Is it pythonic to modify the __eq__() method to:
> 
>    def __eq__(self, other):
>       return self.__head == other._Foo__head and self.__tail == other._Foo__tail

You just need other.__head

> or is this too restrictive (e.g., perhaps I wish to compare a Foo and
> Bar class as correlated list sequences.  It is likely that
> other._Foo__head will fail for a Bar).

It depends on how you want to use Foo. Since it is a toy you can make it do whatever you want.

>>- If you define __eq__() you should also define __ne__(). Alteratively you can
> 
> ... because it seems that Foo1 != Foo2 will fail otherwise.  Why is that?

Because Python doesn't assume that __eq__() and __ne__() are inverses. See
http://docs.python.org/ref/customization.html

Kent



More information about the Tutor mailing list