docs on for-loop with no __iter__?
Alex Martelli
aleaxit at yahoo.com
Mon Sep 6 17:06:06 EDT 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