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