Question about exausted iterators

Christophe chris.cavalaria at free.fr
Fri May 19 05:01:01 EDT 2006


Terry Reedy a écrit :
> "Christophe" <chris.cavalaria at free.fr> wrote in message 
> news:446c2dea$0$5306$626a54ce at news.free.fr...
> 
>>Instead of saying that all works as intended could you be a little
>>helpful and tell me why it was intended in such an obviously broken way
>>instead ?
> 
> 
> I answered both your explicit and implied questions in good faith.  But you 
> seem to be too attached to your pre-judgment to have benefited much, so I 
> won't waste my time and yours saying more.  Instead I suggest that you try 
> this:
> 
> 1. Write a specification for your an alternate, more complicated, iterator 
> protocol.

Specification : same as now except iterators raise once StopIteration 
and any subsequent call to next raises ExaustedIteratorError.

> 2. Write a simple class with .next method that implements your 
> specification.

class ExaustedIteratorError(Exception):
     pass

class safe_iter(object):
     def __init__(self, seq):
             self.it = iter(seq)
     def __iter__(self):
         return self
     def next(self):
             try:
                     return self.it.next()
             except StopIteration:
                     del self.it
                     raise
             except AttributeError:
                 raise ExaustedIteratorError

> 3. Test your class with your example.

 >>> it = safe_iter(range(10))
 >>> print list(it)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 >>> print list(it)
Traceback (most recent call last):
   File "safe_iter_test.py", line 20, in ?
     print list(it)
   File "safe_iter_test.py", line 13, in next
     raise ExaustedIteratorError
__main__.ExaustedIteratorError

> 4. Consider how you would persuade people to add the extra machinery 
> needed.

Well, the main reason for such change is and will always be to catch 
bugs. The fact is, using duct typing is something very common with the 
Python language. And as such, considering a lot of functions which take 
sequences as parameters work as well with an iterator instead, you can 
say that it's an application of duct typing.

The problem is of course the same as for cases. Even if those two objets 
( iterator and container ) look alike from a certain point of view, they 
have some fundamental differences.

So, we have quite a few functions which take freely either a container 
or an iterator, until someone changes that function a little. At that 
point there are three kind errors which happen :
- the function expected a sequence and tries to access it's [] operator 
which fails. Standard duct typing behaviour.
- the function uses the iterator more than once and so, sometimes it 
works without errors but produces an incorrect result.
- the function uses the iterator more than once but never exhausts it's 
values. Same result as above but much harder to catch.

In the sake of avoiding behaviour which lets obvious errors pass and 
produces incorrect results, I propose to change the standard behaviour 
of all the iterators in the standard Python. The change will be so that 
they refuse to be used anymore once they have been exausted. Thus it'll 
help catch the second class. The other procedure used to catch such bugs 
would require explicit typing of the function parameters but this is for 
some other proposal.

> 5. Consider what you would do when people don't.

I'm already doing it. Cleaning up the code, changing parameters names 
around so that it is clear such parameter is an iterator and that other 
one is not, making some functions explicitly refuse iterators etc ... It 
should not that that last feature will be useful even without the 
exhausted iterator guard I propose.

> If you want, post a report on your experiment, and I will read it if I see 
> it.

I suppose I could add safe_iter to the builtins in my project and use it 
around. It would be easy to catch all usages of that one once we settle 
on something different then.



More information about the Python-list mailing list