Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > What makes an iterator an iterator?

Reply
Thread Tools

What makes an iterator an iterator?

 
 
Steven D'Aprano
Guest
Posts: n/a
 
      04-18-2007
I thought that an iterator was any object that follows the iterator
protocol, that is, it has a next() method and an __iter__() method.

But I'm having problems writing a class that acts as an iterator. I have:

class Parrot(object):
def __iter__(self):
return self
def __init__(self):
self.next = self._next()
def _next(self):
for word in "Norwegian Blue's have beautiful plumage!".split():
yield word

But this is what I get:

>>> P = Parrot()
>>> for word in P:

.... print word
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'Parrot'

Why is my instance not an iterator?

But I can do this:

>>> for word in P.next:

.... print word
....
Norwegian
Blue's
have
beautiful
plumage!

I find myself perplexed as to this behaviour.


--
Steven D'Aprano

 
Reply With Quote
 
 
 
 
I V
Guest
Posts: n/a
 
      04-18-2007
On Wed, 18 Apr 2007 15:39:22 +1000, Steven D'Aprano wrote:
> I thought that an iterator was any object that follows the iterator
> protocol, that is, it has a next() method and an __iter__() method.

....
> class Parrot(object):

....
> def __init__(self):
> self.next = self._next()


self.next isn't a method here, it's a generator. You could do:

def __init__(self):
self.next = self._next().next

But, having tested, that doesn't appear to work either - I think
this is because, again, self.next is not strictly a method, it's an
attribute that happens to be callable.

The python manual gives you a possible solution:

---QUOTE http://docs.python.org/lib/typeiter.html ---
Python's generators provide a convenient way to implement the iterator
protocol. If a container object's __iter__() method is implemented as a
generator, it will automatically return an iterator object (technically, a
generator object) supplying the __iter__() and next() methods.
---END QUOTE---

i.e., just rename your _next function to __iter__ . Your class won't
itself be an iterator, but it will be usable in for statements and so one,
and convertable to an iterator with the iter builtin.
 
Reply With Quote
 
 
 
 
Dennis Lee Bieber
Guest
Posts: n/a
 
      04-18-2007
On Wed, 18 Apr 2007 15:39:22 +1000, Steven D'Aprano
<(E-Mail Removed)> declaimed the following in
comp.lang.python:

> I thought that an iterator was any object that follows the iterator
> protocol, that is, it has a next() method and an __iter__() method.
>
> But I'm having problems writing a class that acts as an iterator. I have:
>
> class Parrot(object):
> def __iter__(self):
> return self
> def __init__(self):
> self.next = self._next()
> def _next(self):


>>> class Parrot(object):

.... def __iter__(self):
.... return self.next
.... def __init__(self):
.... self.next = self._next()
.... def _next(self):
.... for word in "Norwegian Blue's have beautiful
plumage!".split():
.... yield word
....
>>> p = Parrot()
>>> for word in p:

.... print word
....
Norwegian
Blue's
have
beautiful
plumage!
>>>

--
Wulfraed Dennis Lee Bieber KD6MOG
http://www.velocityreviews.com/forums/(E-Mail Removed) (E-Mail Removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (E-Mail Removed))
HTTP://www.bestiaria.com/
 
Reply With Quote
 
Ben Finney
Guest
Posts: n/a
 
      04-18-2007
Steven D'Aprano <(E-Mail Removed)> writes:

> class Parrot(object):
> def __iter__(self):
> return self
> def __init__(self):
> self.next = self._next()
> def _next(self):
> for word in "Norwegian Blue's have beautiful plumage!".split():
> yield word


Clearly the problem is you've misused an apostrophe. Python doesn't
like the plural getting an apostrophe.

<URL:http://www.angryflower.com/bobsqu.gif>

--
\ "Speech is conveniently located midway between thought and |
`\ action, where it often substitutes for both." -- John Andrew |
_o__) Holmes, _Wisdom in Small Doses_ |
Ben Finney
 
Reply With Quote
 
Stefan Rank
Guest
Posts: n/a
 
      04-18-2007
on 18.04.2007 07:39 Steven D'Aprano said the following:
> I thought that an iterator was any object that follows the iterator


replace object with "instance of a class", i.e. the relevant methods are
looked up in the __class__ not in the instance (I think).
I had the same troubles trying to dynamically reassign a __call__ method...

> protocol, that is, it has a next() method and an __iter__() method.
>
> But I'm having problems writing a class that acts as an iterator. I have:
>
> class Parrot(object):
> def __iter__(self):
> return self
> def __init__(self):
> self.next = self._next()
> def _next(self):
> for word in "Norwegian Blue's have beautiful plumage!".split():
> yield word


Try this::

class Parrot(object):
def __iter__(self):
return self
def __init__(self):
self.__class__.next = self._next().next # see post by I V
def _next(self):
for word in "Norwegian Blue's have beautiful plumage!".split():
yield word

This works but practically forces the class to be used as a singleton...
not very helpful

Better:

* use the '__iter__ returns/is a generator' way,
* or if you need the object to be the iterator, implement the next
method directly on the class::

class Parrot(object):
def _next(self):
for word in "Norwegian Blue's have beautiful plumage!".split():
yield word
def __iter__(self):
self.generator = self._next()
return self
def next(self):
return self.generator.next()

cheers,
stefan

 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      04-18-2007
On Wed, 18 Apr 2007 16:58:23 +1000, Ben Finney wrote:

> Steven D'Aprano <(E-Mail Removed)> writes:
>
>> class Parrot(object):
>> def __iter__(self):
>> return self
>> def __init__(self):
>> self.next = self._next()
>> def _next(self):
>> for word in "Norwegian Blue's have beautiful plumage!".split():
>> yield word

>
> Clearly the problem is you've misused an apostrophe. Python doesn't
> like the plural getting an apostrophe.
>
> <URL:http://www.angryflower.com/bobsqu.gif>


I thought the rule wa's that any time you 'see an 'S, you put an
apo'strophe before it. If that's wrong, 'shouldn't it rai'se an exception?




--
'Steven D'Aprano


 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      04-18-2007
On Wed, 18 Apr 2007 06:13:39 +0000, I V wrote:

> On Wed, 18 Apr 2007 15:39:22 +1000, Steven D'Aprano wrote:
>> I thought that an iterator was any object that follows the iterator
>> protocol, that is, it has a next() method and an __iter__() method.


[snip]

> i.e., just rename your _next function to __iter__ . Your class won't
> itself be an iterator, but it will be usable in for statements and so one,
> and convertable to an iterator with the iter builtin.



Thanks to all those who helped, this fixed my problem.

For the record, this is what I actually wanted: a four-line self-sorting
dictionary:

class SortedDict(dict):
def __iter__(self):
for key in sorted(self.keys()):
yield key

Note that using sorted(self) does not work.

Iterating over a SortedDictionary returns the keys in sorted order. This
minimalist implementation doesn't sort the values, items or string
representation of the dict, but they should be easy to implement.



--
Steven D'Aprano

 
Reply With Quote
 
Paul McGuire
Guest
Posts: n/a
 
      04-18-2007
On Apr 18, 3:32 am, Steven D'Aprano <(E-Mail Removed)>
wrote:
> On Wed, 18 Apr 2007 06:13:39 +0000, I V wrote:
> > On Wed, 18 Apr 2007 15:39:22 +1000, Steven D'Aprano wrote:
> >> I thought that an iterator was any object that follows the iterator
> >> protocol, that is, it has a next() method and an __iter__() method.

>
> [snip]
>
> > i.e., just rename your _next function to __iter__ . Your class won't
> > itself be an iterator, but it will be usable in for statements and so one,
> > and convertable to an iterator with the iter builtin.

>
> Thanks to all those who helped, this fixed my problem.
>
> For the record, this is what I actually wanted: a four-line self-sorting
> dictionary:
>
> class SortedDict(dict):
> def __iter__(self):
> for key in sorted(self.keys()):
> yield key
>
> Note that using sorted(self) does not work.
>
> Iterating over a SortedDictionary returns the keys in sorted order. This
> minimalist implementation doesn't sort the values, items or string
> representation of the dict, but they should be easy to implement.
>
> --
> Steven D'Aprano


Very neat. Why not this?

class SortedDict(dict):
def __iter__(self):
return iter(sorted(self.keys()))

-- Paul

 
Reply With Quote
 
Peter Otten
Guest
Posts: n/a
 
      04-18-2007
Steven D'Aprano wrote:

> class SortedDict(dict):
> ****def*__iter__(self):
> ********for*key*in*sorted(self.keys()):
> ************yield*key
>
> Note that using sorted(self) does not work.


That's because sorted() invokes __iter__() if present. To prevent the
recursion you can explicitly invoke dict.__iter__():

>>> class SortedDict(dict):

.... def __iter__(self):
.... return iter(sorted(super(SortedDict, self).__iter__()))
....
>>> sd = SortedDict(a=1, b=2, c=3)
>>> list(sd)

['a', 'b', 'c']

Note that a list of keys is still built before the first key is yielded,
and, unlike dict, you can modify your SortedDict while iterating over it:

>>> for k in sd:

.... if k == "b": sd["x"] = 42
....
>>> sd

{'a': 1, 'x': 42, 'c': 3, 'b': 2}

whereas:

>>> d = dict(a=1, b=2, c=3)
>>> for k in d:

.... if k == "b": d["x"] = 42
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

By the way, I think it would be worthwile to change super() to allow
e. g. super(SomeClass, self)[...] as an alternate spelling for
super(SomeClass, self).__getitem__(...) etc. With such an enhancement
SortedDict would become

class SortedDict(dict):
def __iter__(self):
# doesn't work in current Python
iter(sorted(super(SortedDict, self)))


Peter

 
Reply With Quote
 
Georg Brandl
Guest
Posts: n/a
 
      04-18-2007
Stefan Rank schrieb:
> on 18.04.2007 07:39 Steven D'Aprano said the following:
>> I thought that an iterator was any object that follows the iterator

>
> replace object with "instance of a class", i.e. the relevant methods are
> looked up in the __class__ not in the instance (I think).
> I had the same troubles trying to dynamically reassign a __call__ method...


This is correct.

It's not properly documented though, and not applied consistently, e.g.
__enter__ and __exit__ are looked up in the instance itself.

Georg

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How can I overload an iterator/create an iterator Pranav C++ 3 08-08-2008 06:49 AM
List iterator assignment fails, assert iterator not dereferencable David Bilsby C++ 5 10-09-2007 02:05 PM
Difference between Java iterator and iterator in Gang of Four Hendrik Maryns Java 18 12-22-2005 05:14 AM
How to convert from std::list<T*>::iterator to std::list<const T*>::iterator? PengYu.UT@gmail.com C++ 6 10-30-2005 03:31 AM
Iterator doubts, Decision on Iterator usage greg C++ 6 07-17-2003 01:26 PM



Advertisments