On Thu, Jan 23, 2014 at 02:18:33PM -0500, Keith Winston wrote:
> On Thu, Jan 23, 2014 at 1:36 PM, Devin Jeanpierre
> <jeanpierr...@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
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to