[Tutor] iter class
Steven D'Aprano
steve at pearwood.info
Fri Jan 24 01:16:54 CET 2014
On Thu, Jan 23, 2014 at 02:18:33PM -0500, Keith Winston wrote:
> On Thu, Jan 23, 2014 at 1:36 PM, Devin Jeanpierre
> <jeanpierreda at gmail.com> wrote:
>
> > Again, nothing was incorrect about the example. Every iterator has
> > this "problem".
>
> Hmmm. Well, here's what he actually said about that example, since I
> don't think I've explained correctly:
Who is "he"?
> *****
> With iterators, one thing to watch out for is the return of self from
> the __iter__ function. You can all too easily write an iterator that
> isn't as re-usable as you think it is. For example, suppose you had
> the following class:
To be pedantic, an iterator isn't an iterator unless __iter__ returns
self. Let's look at something which is *iterable* but not an iterator: a
lsit. You can iterate over a list, as you know, but iter() (which calls
__iter__ under the hood) does not return the list itself:
py> L = [1, 2, 4, 8]
py> iter(L) is L
False
py> iter(L)
<list_iterator object at 0xb7b77f6c>
Notice that list.__iter__ returns a special list_iterator object. That
is an actual iterator:
py> it = iter(L)
py> iter(it) is it
True
One definining characteristic of an iterator is that you can call iter()
on it as many times as you like, and you'll always get the same object.
[...]
> {my example here}
>
> This works just like you'd expect as long as you create a new object each time:
>
> >>> for i in MyTrickyIter(['a', 'b']):
> ... for j in MyTrickyIter(['a', 'b']):
> ... print i, j
> a a
> a b
> b a
> b b
>
> but it will break if you create the object just once:
>
> {my example here, yielding only a single a b from the above loop}
I think your example was something like this:
it = MyTrickyIter(['a', 'b'])
for i in it:
for j in it:
print i, j
which just prints "a b" and then is done.
This should not be considered a bug in iterators! It's a feature,
working as designed. If you don't want that behaviour, then you
shouldn't use the same iterator in both the outer and inner loops.
For example, if you want to grab items of a sequence in lots of three,
you can do something like this:
py> L = iter(range(12))
py> for a, b, c in zip(L, L, L):
... print(a, b, c)
...
0 1 2
3 4 5
6 7 8
9 10 11
Each time you go around the loop, zip() grabs one item from each
argument. Since the arguments all correspond to the same iterator, that
is equivalent to grabbing three items each time around the loop.
> ******
>
> The difference essentially comes down to the line
>
> ... self.thelist = thelist # in the "broken" example, vs.
But it's not broken at all. Remember that mysterious "list_iterator"
object we saw earlier? That is probably implemented something quite like
this so-called "broken" (not really) example.
> ... self.thelist = iter(thelist) # the "better" way to do it,
> restarts the iterator each time a new loop uses it
But then it isn't an iterator. It's something else. Perhaps something
useful, but not an iterator.
--
Steven
More information about the Tutor
mailing list