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

Tim Peters tim.peters at gmail.com
Fri May 4 23:36:03 EDT 2018

[Nick Coghlan <ncoghlan at gmail.com>]
> ...
> The essence of the given clause concept would be to modify *these specific
> cases* (at least initially) to allow the condition expression to be followed
> by an inline assignment, of the form "given TARGET = EXPR".

I'm not clear on what "these specific cases" are, specifically ;-)
Conditions in "if", "elif", and "while" statement expressions?
Restricted to one "given" clause, or can they chain?  In a listcomp,
is it one "given" clause per "if", or after at most one "if"?  Or is
an "if" even needed at all in a listcomp?  For example,

    [(f(x)**2, f(x)**3) for x in xs]

has no conditions, and

    [(fx := f(x))**2, fx**3) for x in xs]

is one reasonable use for binding expressions.

    [(fx**2, fx**3) for x in xs given fx = f(x)]

reads better, although it's initially surprising (to my eyes) to find
fx defined "at the end".  But no more surprising than the current:

    [(fx**2, fx**3) for x in xs for fx in [f(x)]]


> While the leading keyword would allow TARGET to be an arbitrary assignment
> target without much chance for confusion, it could also be restricted to
> simple names instead (as has been done for PEP 572.

The problem with complex targets in general assignment expressions is
that, despite trying, I found no plausible use case for (at least)
unpacking syntax.  As in, e.g.,

    x, y = func_returning_twople()
    if x**2 + y**2 > 9:  # i.e., distance > 3, but save expensive sqrt

The names can be _unpacked_ in a general assignment expression, but
there appears to be no sane way then to _use_ the names in the test.
This may be as good as it gets:

    if [(x, y := func_returning_twople()). x**2 + y**2 > 9][-1]:

That reminds me of the hideous

    (condition and [T] or [F])[0]

idiom I "invented" long ago to get the effect (in all cases) of the current

    T if condition else F

That was intended to be goofy fun at the time, but I was appalled to
see people later use it ;-)

It''s certain sanest as

    if x**2 + y**2 > 9 given x, y = func_returning_twople():

"given" really shines there!

> With that spelling, the three examples above would become:
>     # Exactly one branch is executed here
>     if m given m = pattern.search(data):
>         ...
>     elif m given m = other_pattern.search(data)):
>         ...
>     else:

Which is OK.  The one-letter variable name obscures that it doesn't
actually reduce _redundancy_, though.  That is, in the current

    match = pattern.search(data)
    if match:

it's obviously less redundant typing as:

    if match := pattern.search(data):


    if match given match = pattern.search(data):

the annoying visual redundancy (& typing) persists.

>     # This name is rebound on each trip around the loop
>     while m given m = pattern.search(remaining_data):

Also fine, but also doesn't reduce redundancy.

>     # "f(x)" is only evaluated once on each iteration
>     result = [(x, y, x/y) for x in data if y given y = f(x)]

As above, the potential usefulness of "given" in a listcomp doesn't
really depend on having a conditional.  Or on having a listcomp
either, for that matter ;-)

    r2, r3 = fx**2, fx**3 given fx = f(x)

One more, a lovely (to my eyes) binding expression simplification
requiring two bindings in an `if` test, taken from real-life code I
happened to write during the PEP discussion:

    diff = x - x_base
    if diff:
        g = gcd(diff, n)
        if g > 1:
            return g

collapsed to the crisp & clear:

    if (diff := x - x_base) and (g := gcd(diff, n)) > 1:
        return g

If only one trailing "given" clause can be given per `if` test
expression, presumably I couldn't do that without trickery.  If it's
more general,

    if (diff given diff = x _ xbase) and g > 1 given g = gcd(diff, n):

reads worse to my eyes (perhaps because of the "visual redundancy"
thing again), while

   if diff and g > 1 given diff = x - x_base given g = gcd(diff, n):

has my eyes darting all over the place, and wondering which of the
trailing `given` clauses executes first.

> ...

More information about the Python-ideas mailing list