What makes an iterator an iterator?
steve at holdenweb.com
Thu Apr 19 13:37:45 CEST 2007
> 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
>> 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
[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")
<open file 'travel.txt', mode 'r' at 0x7ff1f6e0>
'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
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.
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