[Python-Dev] PEP 340 -- loose ends

Delaney, Timothy C (Timothy) tdelaney at avaya.com
Tue May 3 04:53:03 CEST 2005


Guido van Rossum wrote:

> [Delaney, Timothy]
>> PEP 340 does not address "normal" iterators very well, but a
>> properly-constructed iterator will behave correctly.
> 
> This is by design.

Yep - I agree.

>> The PEP though is very generator-focussed.
> 
> Disagree. The PEP describes most everything (e.g. the block statement
> semantics) in terms of iterators, and then describes how the new APIs
> behave for generators.

Again, agree. What I meant is that there are no examples of how to
actually implement the correct semantics for a normal iterator. Doing it
right is non-trivial, especially with the __next__ and __exit__
interaction (see below).

>> The issues I see for "normal"
>> iterators (and that need to be addressed/stated in the PEP) are:
>> 
>>     1. No automatic handling of parameters passed to __next__ and
>>        __exit__. In a generator, these will raise at the yield
>>        -statement or -expression. A "normal" iterator will have
>>        to take care of this manually. 
> 
> Not sure what you mean by this. If __next__() is defined, it is passed
> the parameter; if only next() is defined, a parameter (except None) is
> an error. That seems exactly right. Also, if __exit__() isn't defined,
> the exception is raised, which is a very sensible default behavior
> (and also what will happen to a generator that doesn't catch the
> exception).

What I meant is how the iterator is meant to handle the parameters
passed to each method. PEP 340 deals with this by stating that
exceptions will be raised at the next yield-statement or -expression. I
think we need an example though of how this would translate to a
"normal" iterator. Something along the lines of::

    class iterator (object):

        def next (self):
            return self.__next__()

        def __next__(self, arg=None):
            value = None

            if isinstance(arg, ContinueIteration):
                value = arg.value
            elif arg is not None:
                raise arg

            if value is None:
                raise StopIteration

            return value

        def __exit__(self, type=None, value=None, traceback=None):
            if (type is None) and (value is None) and (traceback is
None):
                type, value, traceback = sys.exc_info()

            if type is not None:
                try:
                    raise type, value, traceback
                except type, exc:
                    return self.__next__(exc)
 
           return self.__next__()

>> As another option, it might be worthwhile creating a base iterator
type
>> with "correct" semantics.

> Well, what would the "correct" semantics be? What would passing a
> parameter to a list iterator's __next__() method mean?

Sorry - I meant for user-defined iterators. And the correct semantics
would be something like the example above I think. Except that I think
most of it would need to be in a separate method (e.g. _next) for base
classes to call - then things would change to be something like::

    class iterator (object):
        ...

        def _next (self, arg):
            if isinstance(arg, ContinueIteration):
                return arg.value
            elif arg is not None:
                raise arg

        def __next__(self, arg=None):
            value = self._next(arg)

            if value is None:
                raise StopIteration

            return value            

        ...

Finally, I think there is another loose end that hasn't been addressed::

    When __next__() is called with an argument that is not None, the
    yield-expression that it resumes will return the value attribute
    of the argument.  If it resumes a yield-statement, the value is
    ignored (or should this be considered an error?).  When the
    *initial* call to __next__() receives an argument that is not
    None, the generator's execution is started normally; the
    argument's value attribute is ignored (or should this be
    considered an error?).  When __next__() is called without an
    argument or with None as argument, and a yield-expression is
    resumed, the yield-expression returns None.

My opinion is that each of these should be an error.

Tim Delaney


More information about the Python-Dev mailing list