Mailing List Archive

Automatically advancing a bi-directional generator to the point of accepting a non-None value?
Suppose we write a very simple bi-directional generator in Python:

def share_of_total():
s = 0
new_num = 0
while True:
new_num = yield new_num / (s or 1)
s += new_num

share_calculator = share_of_total()
next(share_calculator) # Without this we get the TypeError

for num in [1, 2, 3]:
print(share_calculator.send(num))

This generator just accepts a number and yields a float representing
its share of the sum of all previously provided numbers.

We would ideally like to just use it immediately as follows:

share_calculator = share_of_total()
print(share_calculator.send(num))

However, this causes `TypeError: can't send non-None value to a
just-started generator`. All users of the `share_of_total()` must
remember to execute `next(share_calculator)` before the generator is
usable as intended.

Is there an elegant way to make `share_calculator` immediately usable
- i.e. to be able to immediately call `share_calculator.send(num)`
after creating `share_calculator`?

I know it can be done with a fairly trivial wrapper, but I was hoping
for a more elegant solution that doesn't require boilerplate.
--
https://mail.python.org/mailman/listinfo/python-list
Re: Automatically advancing a bi-directional generator to the point of accepting a non-None value? [ In reply to ]
Go Luhng wrote at 2020-11-21 14:30 -0500:
>Suppose we write a very simple bi-directional generator in Python:
>
> def share_of_total():
> s = 0
> new_num = 0
> while True:
> new_num = yield new_num / (s or 1)
> s += new_num
>
> share_calculator = share_of_total()
> next(share_calculator) # Without this we get the TypeError
>
> for num in [1, 2, 3]:
> print(share_calculator.send(num))
>
>This generator just accepts a number and yields a float representing
>its share of the sum of all previously provided numbers.
>
>We would ideally like to just use it immediately as follows:
>
> share_calculator = share_of_total()
> print(share_calculator.send(num))
>
>However, this causes `TypeError: can't send non-None value to a
>just-started generator`. All users of the `share_of_total()` must
>remember to execute `next(share_calculator)` before the generator is
>usable as intended.

When you want to automate things, you write a function.

In your case, you have a generator function
and you want that this generator function is called,
then `next` is applied to the resulting generator;
the resulting generator is what you actually want.

From this description, it should be easy to define a function
which does precisely this for you.

If you find that you regularly want to automate the initial `next`,
you can implement your function as a so called "decorator",
say `auto_next`. Then you can define generator functions
with automatic `next` via:

@auto_next
def gen_fun(...): ...
--
https://mail.python.org/mailman/listinfo/python-list