# [Python-ideas] Inline assignments using "given" clauses

Steven D'Aprano steve at pearwood.info
Sat May 12 03:37:28 EDT 2018

```On Sat, May 12, 2018 at 02:37:20AM +0100, Rob Cliffe via Python-ideas wrote:

> Yeah, well, I'm totally lost.  Even after trying out this code, and
> refactoring it once if not twice, I didn't understand it.  I don't know
> what point you're trying to prove, but you seem to have comprehensively
> proved it.

Do you mean Serhiy's example of currently supported syntax?

smooth_signal = [average for average in [0] for x in signal for average in [(1-decay)*average + decay*x]]

It helps if you know the algorithm for exponential smoothing: for
each value x (aside from the first), the average is equal to a mix
of the current value x and the previous average A, split by some
proportion P:

A = (1-P)*A + P*x

If P is 0.5, that is equivalent to taking the ordinary average between
the current value and the previous average:

A = (A+x)/2  # when P == 0.5

In the comprehension, P is called "decay" and A is called "average":

average = (1-decay)*average + decay*x

Writing the comprehension as a single line is hard to read. Let's give
it some structure:

smooth_signal = [average # append average to the results
for average in [0]
for x in signal
for average in [(1-decay)*average + decay*x]
]

Horrible as it is, it is perfectly legal Python right now. It uses

for name in SINGLE_ITEM_LIST

to perform an assignment. So that's equivalent to:

average = 0
for x in signal
average = (1-decay)*average + decay*x
append average to the results

Pull the initial value of average out of the comprehension, and use the
PEP 572 syntax:

average = 0
smooth_signal = [(average := (1-decay)*average + decay*x) for x in signal]

which is a huge improvement in my opinion. It would be more obvious if
the expression being calculated came first:

smooth_signal = [(1-decay)*average + decay*x as average for x in signal]

but there are good reasons why the "as" syntax won't work. So it looks
like we're stuck with needing to look ahead past the := to see the
actual value being appended to the list.

A minor inconvenience, equivalent to that in ternary if, where we have
to look ahead to see the condition:

[target := COMPREHENSION_VALUE for x in sequence]

true_value if CONDITION else false_value

So I expect that it will take me a little while to learn to look ahead
and read binding-expressions fluently. (Like comprehensions themselves,
really. It took me a few months to stop needing to pull them apart to
understand them.)

He's Nick's version, as best as I am able to tell:

average = 0
smooth_signal = [(average given average = (1-decay)*average + decay*x) for x in signal]

So we have the same look-ahead needed to see the expression we care
about, but instead of merely having two characters := needed to do the
binding, we need "given average =".

--
Steve
```