Terry Reedy a écrit :
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.