Hi,
Thanks for the responses.
7stud said:
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
------
...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:
P.next()
is entirely different from the name look up for 'next' when you call:
type(P).next().
In the first lookup, the instance attribute "next" hides the class
attribute "next". In the second lookup, the "next" attribute of the
class is returned, i.e. the next() method. Yet, a generator-function-
call returns an iterator object that wraps the generator function, so
when the for loop makes repeated calls to type(P).next(), an iterator
object is repeatedly returned--what you want is i.next() to be
returned.
I suspected next() might be called through the class, but after
carefully parsing the section on iterators (Python in a Nutshell, p.
65) where it says iter() returns an object i, and then the for loop
repeatedly calls i.next(), I dismissed that idea. I have to admit I'm
still a little confused by why you only parenthetically noted that
information and called it pedantic.
One very good way to get an iterator from an iterable is for .__iter__ to
be a generator function.
Ahhh. That eliminates having to deal with next().next constructs.
Nice.
snip all examples of bad code that violate the iterator rule
by improperly writing .next as a generator function
What iterator rule states that .next can't be a generator function?
My book says an iterator is any object with a .next method that is
callable without arguments (Python in a Nutshell(p.65) says the same
thing). Also, my boos says an iterable is any object with an
__iter__ method. As a result, next() and __iter__() don't have to
be in the same object:
lass MyIterator(object):
def __init__(self, obj):
self.toIterateOver = obj
def next(self):
for x in range(self.toIterateOver.age):
print x
raise StopIteration
class Dog(object):
def __init__(self, age):
self.age = age
def __iter__(self):
return MyIterator(self)
d = Dog(5)
for year in d:
print year
I've read recommendations that an iterator should additionally contain
an __iter__() method, but I'm not sure why that is. In particular PEP
234 says:
----------
Classes can define how they are iterated over by defining an
__iter__() method; this should take no additional arguments and
return a valid iterator object. A class that wants to be an
iterator should implement two methods: a next() method that
behaves
as described above, and an __iter__() method that returns self.
The two methods correspond to two distinct protocols:
1. An object can be iterated over with "for" if it implements
__iter__() or __getitem__().
2. An object can function as an iterator if it implements next().
Container-like objects usually support protocol 1. Iterators are
currently required to support both protocols. The semantics of
iteration come only from protocol 2; protocol 1 is present to make
iterators behave like sequences; in particular so that code
receiving an iterator can use a for-loop over the iterator.
Classes can define how they are iterated over by defining an
__iter__() method; this should take no additional arguments and
return a valid iterator object. A class that wants to be an
iterator should implement two methods: a next() method that
behaves
as described above, and an __iter__() method that returns self.
The two methods correspond to two distinct protocols:
1. An object can be iterated over with "for" if it implements
__iter__() or __getitem__().
2. An object can function as an iterator if it implements next().
Container-like objects usually support protocol 1. Iterators are
currently required to support both protocols. The semantics of
iteration come only from protocol 2; protocol 1 is present to make
iterators behave like sequences; in particular so that code
receiving an iterator can use a for-loop over the iterator.
--------
The semantics of
iteration come only from protocol 2
My example demonstrates that.
protocol 1 is present to make
iterators behave like sequences; in particular so that code
receiving an iterator can use a for-loop over the iterator.
I don't understand that part--it looks like my example is using a for
loop over the iterator.