On Sat, May 25, 2013 at 10:38 PM, Steven D'Aprano <st...@pearwood.info> wrote: > On 26/05/13 05:23, Mark Lawrence wrote: >> >> On 25/05/2013 19:56, Jim Mooney wrote: >>> >>> I thought tuples were immutable but it seems you can swap them, so I'm >>> confused: >>> >>> a,b = 5,8 >> >> >> You've defined two names a and b so where's the tuple? > > > On the right hand side, 5,8 creates a tuple, which is then immediately > unpacked to two individual values. You can see this by disassembling the > code. In 2.7, you get this:
The abstract source tree (AST) uses a tuple or list for sequences in "targets" (multiple targets are generated for chained assignment) and also for the "value". The tuples and lists for the target(s) obviously aren't preserved in the source code. They guide the compiler in the arrangement of UNPACK_SEQUENCE and STORE operations. A BUILD_TUPLE / BUILD_LIST operation for the right-hand side is generally preserved for the "value" tuple or list. One optimization in CPython, as you've shown, is to store a tuple of constants in the code object. Also, if the length of the sequence is less than 4, CPython's peephole optimizer will replace the BUILD/UNPACK with simple stack rotation(s). For length 2 it replaces it with a ROT_TWO. For length 3 it uses a ROT_THREE followed by a ROT_TWO. For example: def f(): a,b,c = x,y,z >>> dis.dis(f) 2 0 LOAD_GLOBAL 0 (x) 3 LOAD_GLOBAL 1 (y) 6 LOAD_GLOBAL 2 (z) 9 ROT_THREE 10 ROT_TWO 11 STORE_FAST 0 (a) 14 STORE_FAST 1 (b) 17 STORE_FAST 2 (c) 20 LOAD_CONST 0 (None) 23 RETURN_VALUE In the original bytecode, before the optimization, there was a BUILD_TUPLE(3) followed immediately by UNPACK_SEQUENCE(3). The LOAD operations set the stack as [x,y,z], where z is at the top of the stack, which sets up for BUILD_TUPLE. But UNPACK_SEQUENCE would unpack in reverse, leaving the stack as [z,y,x] to be popped off into the names a, b, and c. So the optimizer inserts a ROT_THREE to get [x,y,z] => [z,x,y] and then ROT_TWO to get [z,x,y] => [z,y,x]. > An interesting fact: on the left hand side, Python is very flexible with its > sequence unpacking syntax. All of these are equivalent, where RHS (Right > Hand Side) evaluates to exactly two items: Unpacking in assignment allows an almost comical level of complexity, not even including subscription and attribute targets that can use expressions for the primary object and subscript. A 'simple' example: def f(arg): i0,(i1,i2),(i3,(i4,i5,i6)) = arg return i0,i1,i2,i3,i4,i5,i6 >>> f([0,'ab',[1,'cde']]) (0, 'a', 'b', 1, 'c', 'd', 'e') Here's the AST for this silliness: >>> src = 'i0,(i1,i2),(i3,(i4,i5,i6)) = arg' >>> ast.dump(ast.parse(src).body[0]) I formatted the output a bit: Assign( targets=[ Tuple( elts=[ Name(id='i0', ctx=Store()), Tuple( elts=[ Name(id='i1', ctx=Store()), Name(id='i2', ctx=Store())], ctx=Store()), Tuple( elts=[ Name(id='i3', ctx=Store()), Tuple( elts=[ Name(id='i4', ctx=Store()), Name(id='i5', ctx=Store()), Name(id='i6', ctx=Store())], ctx=Store())], ctx=Store())], ctx=Store())], value=Name(id='arg', ctx=Load())) _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor