On Tue, Apr 6, 2021 at 3:26 AM Rob Cliffe via Python-list
<python-list@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@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
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to