Friday Finking: initialising values and implied tuples

Chris Angelico rosuav at gmail.com
Mon Apr 5 13:33:44 EDT 2021


On Tue, Apr 6, 2021 at 3:26 AM Rob Cliffe via Python-list
<python-list at python.org> wrote:
>
>
>
> On 05/04/2021 17:52, Chris Angelico wrote:
> > On Tue, Apr 6, 2021 at 2:32 AM Rob Cliffe via Python-list
> > <python-list at python.org> wrote:
> >>
> >>
> >> It doesn't appear to, at least not always.  In Python 3.8.3:
> >> from dis import dis
> >> def f(): x = 1 ; y = 2
> >> def g(): (x,y) = (1,2)
> >> dis(f)
> >> dis(g)
> >>
> >> Output:
> >>     2           0 LOAD_CONST               1 (1)
> >>                 2 STORE_FAST               0 (x)
> >>                 4 LOAD_CONST               2 (2)
> >>                 6 STORE_FAST               1 (y)
> >>                 8 LOAD_CONST               0 (None)
> >>                10 RETURN_VALUE
> >>     3           0 LOAD_CONST               1 ((1, 2))
> >>                 2 UNPACK_SEQUENCE          2
> >>                 4 STORE_FAST               0 (x)
> >>                 6 STORE_FAST               1 (y)
> >>                 8 LOAD_CONST               0 (None)
> >>                10 RETURN_VALUE
> >> Thinking some more about this, this (removing the tuples) is not a
> >> straightforward optimisation to do.
> > It's important to be aware of the semantics here. Saying "x = 1; y =
> > 2" requires that x be set before 2 is calculated (imagine if it had
> > been "y = x + 2" or something), whereas "x, y = 1, 2" has to do the
> > opposite, fully evaluating the right hand side before doing any of the
> > assignments.
> >
> >> I guess it's safe if the RHS is a tuple containing only
> >>       constants, by which I think I mean number/string literals and
> >> built-in constants (None, True etc.).
> >>       variables (NOT expressions containing variables such as "z+1")
> >> which do not occur on the LHS
> >>       tuple/list/dictionary/set displays which themselves contain only
> >> the above, or nested displays which themselves ... etc.
> > Nope, there's no "it's safe if" other than constants - which are
> > already handled differently.
> How are constants handled differently (apart from using LOAD_CONST)?
> See my dis example above.
> >   If there is ANY Python code executed to
> > calculate those values, it could depend on the previous assignments
> > being completed.
> I don't understand.  What semantic difference could there be between
>      x = { 1: 2 }    ;    y = [3, 4]   ;   z = (5, 6)
> and
>      x, y, z = { 1:2 }, [3, 4], (5, 6)
> ?  Why is it not safe to convert the latter to the former?
> But I withdraw "set" from my "safe" list because I now realise that
> "set" could be reassigned.

Firstly, anything with any variable at all can involve a lookup, which
can trigger arbitrary code (so "variables which do not occur on the
LHS" is not sufficient). But in general, it's not safe to do too many
order-of-evaluation changes when it's not actual literals. The only
exception would be, as you put in this particular example,
list/dict/set display, but NOT the name "set" (so you can't make an
empty set this way). So basically, it's literals, and things that
people treat like literals; otherwise, the order of evaluation has to
be maintained.

One good way to get an idea for which non-literals are "likely to be
safe" (in scare quotes because, honestly, it's REALLY HARD to know
what's actually safe) would be to look at ast.literal_eval; if it
would accept the expression, it's quite probably safe. But even that
isn't actually a perfect indication:

>>> set = lambda: print("Wat")
>>> eval("set()")
Wat
>>> ast.literal_eval("set()")
set()

Generally, unless you can mathematically prove that there's absolutely
no way the result could possibly be different, it's best to maintain
the correct order of evaluation.

ChrisA


More information about the Python-list mailing list