[Tutor] __getitem__() and for loops
Danny Yoo
dyoo at hkn.eecs.berkeley.edu
Sat Oct 11 03:51:00 EDT 2003
> >Is a for loop actually calling __getitem__() behind the scenes, and
> >passing in the current index of the range of the for loop?
>
> Python looks first for __iter__ then tries __getitem__ -- also for
> backwards compatibility. Before the iterator protocol (< 2.2) coding a
> __getitem__ was the standard way to make classes cooperate with
> for-loops.
Hello!
Hmmm... Ideally, we'd be able to find out the specific details about the
'for' statement from the Language Reference:
http://www.python.org/doc/current/ref/for.html
Although the text does mention "iterable" objects, for the most part, the
description emphasizes the idea that the for loop uses the 'sequence'
interface (__getitem__()) to reach every element in the 'expression list'.
So this part of the documentation may need to be freshened up a bit to
better reflect the current reality. *grin*
Let's dig in a little bit more. The documentation from the Iterator PEP:
http://www.python.org/peps/pep-0234.html
seems to have a more up-to-date description on how 'for' loops are working
now:
"""For backwards compatibility, the PyObject_GetIter() function
implements fallback semantics when its argument is a sequence that
does not implement a tp_iter function: a lightweight sequence
iterator object is constructed in that case which iterates over
the items of the sequence in the natural order."""
There are some low-level C details in that paragraph, but it's not too
bad. In general, for loops uses iter() now. But if the thing we're
iterating across doesn't natively support iteration, the system generates
a iterable wrapper on-the-fly around that sequence. And that wrapper is
responsible for doing the right __getitem__()'s to make the iteration
work.
So the subtle thing to see is that it's not 'for' loops themselves that
call __getitem__() anymore. Instead, it's the result of that wrapper that
iter() is generating on-the-fly:
###
>>> class Wrapper:
... def __init__(self, obj):
... self.obj = obj
... def __getattr__(self, attr):
... print "Debug: attribute", attr, "requested"
... return getattr(self.obj, attr)
...
>>> class Foo: pass
...
>>> f = Wrapper(Foo())
>>> iter(f)
Debug: attribute __iter__ requested
Debug: attribute __getitem__ requested
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: iteration over non-sequence
###
Hope this helps!
More information about the Tutor
mailing list