docs on for-loop with no __iter__?

Alex Martelli aleaxit at yahoo.com
Mon Sep 6 23:06:06 CEST 2004


Steven Bethard <steven.bethard at gmail.com> wrote:

> Andrew Dalke <adalke <at> mindspring.com> writes:
> > What I wanted was forward iteration
> > in Python 1.x.  It happened that forward iteration was
> > implemented only on top of indexing, so I had to hijack the
> > indexing mechanism to get what I wanted.  But I never thought
> > of it as "x[i] indexing" only "the hack needed to get forward
> > iteration working correctly."
> 
> Good, that reaffirms my intuition that you didn't really want the __getitem__
> behavior (eg x[i] access) -- it was just the only way to get the __iter__
> behavior too.

Yes, it used to be.


> Would it break old code if the __getitem__ iterator checked for a __len__
> method and tried to use it if it was there?  It just seems like if you already

Yes, it would break old code wherever the old code had a __len__ method
returning a value not congruent with the index value for which
__getitem__ raises IndexError.  That's possibly weird old code, but why
should it get broken?

Consider __len__ used to be a popular way to let your instances be
usable in a boolean context -- I believe __nonzero__ was introduced
later.  So, take a class which only know whether it's empty or not, it
could have a __len__ that only returns 0 (==empty) or  1(==nonempty),
and still allow proper iteration by only raising in __getitem__ when all
items have been iterated on.  If loops took account of __len__ suddenly
all that old code would break.  Maybe there's much and maybe there's
little, but why break ANY of it?!

> know you're creating a sequence type and you have a __len__ and a __getitem__,
> then you've already provided all the necessary information for iteration.  Why
> should you have to define an __iter__ or throw IndexErrors in your
__getitem__?

Because you used to be allowed to return from __len__ a value not
congruent with the index value for which __getitem__ raises IndexError,
and changing that might break old code.  As to whether that legacy
protocol was optimal, I think not -- today's __iter__ is clearly better,
simpler and faster.  If you have many classes which define __getitem__
and __len__ and want to iterate on them all, make a mixin class:

class MixinLenwiseIterator:
    def __iter__(self):
        return LenwiseIterator(self)

class LenwiseIterator(object):
    def __iter__(self):
        return self
    def __init__(self, seq):
        self.seq = seq
        self.i = 0
    def next(self):
        if self.i >= len(self.seq):
            raise StopIteration
        result = self.seq[self.i]
        self.i += 1
        return result

Just add MixinLenwiseIterator to your sequence classes' bases and be
happy.


Alex



More information about the Python-list mailing list