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

Reply via email to