Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Language improvement: Get more from the `for .. else` clause

50 views
Skip to first unread message

Victor Savu

unread,
Jun 27, 2016, 12:52:39 AM6/27/16
to
tl;dr: 1. Add `StopAsyncIteration.value`, with the same semantic as
`StopIteration.value` (documented in PEP 380).
2. Capture `StopIteration.value` and StopAsyncIteration.value in the
`else` clauses of the `for` and `async for` statements respectively.
Note: I already have a proof-of-concept implementation:
repository: https://github.com/Victor-Savu/cpython
branch: feat/else_capture


Dear members of the Python list,

I am writing to discuss and get the community's opinion on the following two
ideas:

1. Capture the `StopIteration.value` in the `else` clause of the `for ..
else`
statement:

Generators raise StopIteration on the return statement. The exception
captures the return value. The `for` statement catches the
`StopIteration`
exception to know when to jump to the optional `else` statement, but
discards the enclosed return value.

I want to propose an addition to the Python syntax which gives the
option
to capture the return value in the `else` statement of the `for` loop:

```
def holy_grenade():
yield 'One ...'
yield 'Two ...'
yield 'Five!'
return ('Galahad', 'Three')

for count_ in holy_grenade():
print("King Arthur: {count_}")
else knight, correction: # << new capture syntax here
print(f"{knight}: {correction}, Sir!")
print(f"King Arthur: {correction}!")
```

prints:
```
King Arthur: One ...
King Arthur: Two ...
King Arthur: Five!
Galahad: Three, Sir!
King Arthur: Three!
```

Of course, the capture expression is optional, and omitting it preserves
the current behavior, making this proposed change backwards compatible.
Should the iterator end without raising the StopIteration exception,
the value `None` will be implicitly passed to the capture expression. In
the example above, this will result in:
```
TypeError: 'NoneType' object is not iterable
```
because of the attempt to de-structure the result into `knight` and
`correction`.

2. Add a `StopAsyncIteration.value` member which can be used to transfer
information about the end of the asynchronous iteration, in the same way
the `StopIteration.value` member is used (as documented in PEP 380).
Capture this value in the in the else clause of the `async for`
statement
in the same way as proposed for the `StopIteration.value` in the
previous
point.

You can find a working proof-of-concept implementation of the two proposed
changes in my fork of the semi-official cpython repository on GitHub:
repository: https://github.com/Victor-Savu/cpython
branch: feat/else_capture

Disclaimer: My Internet searching skills have failed me and I could not find
any previous discussion on any of the two topics. If you are aware of such
discussion, I would be grateful if you could point it out.

I look forward to your feedback, ideas, and (hopefully constructive)
criticism!


Best regards,
Victor

Michael Selik

unread,
Jun 27, 2016, 11:06:48 AM6/27/16
to
On Mon, Jun 27, 2016 at 12:53 AM Victor Savu <victor.ni...@gmail.com>
wrote:

> capture the [StopIteration] value in the `else` statement of the `for` loop
>

I'm having trouble thinking of a case when this new feature is necessary.
Can you show a more realistic example?

Victor Savu

unread,
Jun 29, 2016, 7:11:35 AM6/29/16
to
Sure.

Simple use-case: Decorate the yielded values and the return value of a
generator. Right now, with `yield from` you can only decorate the return
value, whereas with a for loop you can decorate the yielded values, but you
sacrifice the returned value altogether.

```
def ret_decorator(target_generator):
returned_value = yield from target_generator()
return decorate_ret(returned_value)

def yield_decorator(target_generator):
for yielded_value in target_generator:
yield decorate_yield(yielded_value)

# Nothing to return. the target return value was
# consumed by the for loop
```

With the proposed syntax, you can decorate both:

```
def decorator(target_generator):
for yielded_value in target_generator:
yield decorate_yield(yielded_value)
else returned_value:
return decorate_ret(returned_value)
```

Please let me know if you are interested in a more concrete case such as a
domain-specific application (I can think of progress bars, logging,
transfer rate statistics ...).

Best,
VS

On Mon, Jun 27, 2016 at 5:06 PM, Michael Selik <michae...@gmail.com>
wrote:

Michael Selik

unread,
Jun 29, 2016, 11:24:41 AM6/29/16
to
On Wed, Jun 29, 2016 at 7:11 AM Victor Savu <victor.ni...@gmail.com>
wrote:

> Please let me know if you are interested in a more concrete case such as a
> domain-specific application (I can think of progress bars, logging,
> transfer rate statistics ...).
>

Yes, please. I'd like to compare the proposed syntax against the solution
with the current syntax.
0 new messages