What makes an iterator an iterator?

Steve Holden steve at holdenweb.com
Thu Apr 19 13:37:45 CEST 2007


7stud wrote:
> Hi,
> 
> Thanks for the responses.
> 
>> 7stud <bbxx789_0... at yahoo.com> wrote:
>>> Can you explain some of the details of why this code fails:
>> ---
>> class Parrot(object):
>>     def __iter__(self):
>>         return self
>>     def __init__(self):
>>         self.next = self.next().next
>>     def next(self):
>>         for word in "Norwegian Blue's have beautiful
>> plumage!".split():
>>             yield word
>>
>> P = Parrot()
>> for word in P:
>>     print word
>> ------
> 
> On Apr 18, 8:45 pm, a... at mac.com (Alex Martelli) wrote:
>> ...a loop like "for x in y:" binds an unnamed temporary
>> variable (say _t) to iter(y) and then repeatedly calls _t.next() [or to
>> be pedantic type(_t).next(t)] until that raises StopIteration.
> 
> 
> Aiiii.  Isn't this the crux:
> 
>> repeatedly calls....[type(_t).next(t)]
> 
>  As far as I can tell, if the call was actually _t.next(), the code I
> asked about would work.  However, the name look up for 'next' when you
> call:
> 
[snip wild goose chase that appears to miss the main point].

It's nothing to do with the name lookup. Alex mentioned that to remind 
us that the magic "double-under" names are looked up on the type rather 
than the instance, so messing around with instances won't change the 
behavior. [This is not true of "old-style" or "classic" classes, which 
we should be eschewing in preparation for their disappearance].

You have to understand the iterator protocol, which is how the language 
interacts with objects whose contents it iterates over (for example in 
for loops). When you iterate over an object X then the *interpreter*, 
under the hood, initializes the loop by calling iter(X) and stashing the 
result away as, let's say, _t. Every time a new value is needed in the 
iteration _t.next() is called to produce it.

We can see this if we open a file:

  >>> f = open("travel.txt")
  >>> f.__iter__()
<open file 'travel.txt', mode 'r' at 0x7ff1f6e0>
  >>> f.next()
'Virgin Flight booking extension 33024 Louise reference 1VV75R\r\n'
  >>>

Calling the file's .next() method produces the next line in the file.

The point is that a function with "yield" expressions in it, when 
called, returns a generator object. So if an instance's next() method 
contains yield statements then repeated calls to it give you an 
(endless) sequence of generator objects.

Here's a simple object class that adheres to the iterator protocol:

  >>> class myI(object):
  ...   def __init__(self, lim):
  ...     self.lim = lim
  ...     self.current = 0
  ...   def __iter__(self):
  ...     return self
  ...   def next(self):
  ...     self.current += 1
  ...     if self.current > self.lim:
  ...       raise StopIteration
  ...     return self.current # NOT yield!
  ...
  >>> myi = myI(5)
  >>> for i in myi:
  ...   print i
  ...
1
2
3
4
5
  >>>

I hope this helps. You appear to be forming a rather over-complex model 
of the way Python behaves. Think "simple" - Python tries to be as simple 
as it can to achieve its objectives.

regards
  Steve
-- 
Steve Holden       +44 150 684 7255  +1 800 494 3119
Holden Web LLC/Ltd          http://www.holdenweb.com
Skype: holdenweb     http://del.icio.us/steve.holden
Recent Ramblings       http://holdenweb.blogspot.com




More information about the Python-list mailing list