docs on for-loop with no __iter__?
Paul McGuire
ptmcg at austin.rr._bogus_.com
Sat Sep 4 17:23:21 EDT 2004
"Andrew Dalke" <adalke at mindspring.com> wrote in message
news:bMp_c.7491$w%6.1810 at newsread1.news.pas.earthlink.net...
<snip>
>
> Looking at the language reference from CVS, I found
> http://www.python.org/dev/doc/devel/ref/for.html
>
> It states
>
> ] The suite is then executed once for each item in
> ] the sequence, in the order of ascending indices.
>
> That implies the sequence is indexed, yes? But if
> the sequence implements __iter__ then there's no
> possibly no underlying idea of 'index'.
>
> Should this be fixed?
>
> Andrew
> dalke at dalkescientific.com
Section 7.3 (from the link given above) gives the syntax for "for" as:
for_stmt ::= "for" target_list "in" expression_list ":" suite
["else" ":" suite]
and then begins describing the component syntax elements as, "The expression
list is evaluated once; it should yield a sequence." This seems to be a bit
dated, since expression_list could also be a generator or iterator.
Additionally, "for" uses an adaptive method to try to simulate an iterator
if no __iter__ method is provided, by successively calling __getitem__ until
IndexError is raised (this error gets silently absorbed within this
pseudo-iterator).
Here is a simple test class: (I also implemented __len__ thinking that it
would be used to limit the calls to __getitem__, but we can see from the
output that it is never called - instead __getitem__ gets called one time
too many, telling the pseudo-iterator that there are no more entries).
class A(object):
def __init__(self,lst):
self.list_ = lst[:]
def __len__(self):
print self.__class__.__name__+".__len__"
return len(self.list_)
def __getitem__(self,i):
print self.__class__.__name__+".__getitem__"
return self.list_[i]
class A_with_iter(A):
def __iter__(self):
print self.__class__.__name__+".__iter__"
return iter(self.list_)
for cls in (A, A_with_iter):
a = cls([1,2,3])
print "iterate over %s" % cls.__name__
for i in a:
print i
print
output:
iterate over A
A.__getitem__
1
A.__getitem__
2
A.__getitem__
3
A.__getitem__
iterate over A_with_iter
A_with_iter.__iter__
1
2
3
Note that this is the basis for the recently reported bugs in email.Message
and cgi.FieldStorage. email.Message does not implement __iter__, and its
__getitem__ method assumes that items will be retrieved like keyed
dictionary lookups, not using integer sequence access. So when __getitem__
calls .lower() on the input string, Python throws an error - you can't do
lower() on an int.
> >>> em = email.message_from_file(open('MAILMESSAGE'))
> >>> for i in em:
> ... print i
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "/usr/lib/python2.3/email/Message.py",
> line 304, in __getitem__
> File "/usr/lib/python2.3/email/Message.py",
> line 370, in get
> AttributeError: 'int' object has no attribute 'lower'
-- Paul
More information about the Python-list
mailing list