[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