On Tue, Apr 6, 2021 at 3:26 AM Rob Cliffe via Python-list
<[email protected]> wrote:
>
>
>
> On 05/04/2021 17:52, Chris Angelico wrote:
> > On Tue, Apr 6, 2021 at 2:32 AM Rob Cliffe via Python-list
> > <[email protected]> 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