[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
More information about the Python-ideas
mailing list