19

Code:

x="salil"
y="Ajay"
y,x=x,y

print x +" "+ y

Output:

Ajay salil

What is a logical explanation for this?

martineau
  • 119,623
  • 25
  • 170
  • 301
salil vishnu Kapur
  • 660
  • 1
  • 6
  • 29

3 Answers3

59

The way this mechanism works is a combination of two features -- forming implicit tuples, and tuple/list unpacking.

When you do something = x, y, what Python will do is implicitly create a tuple (a sort of immutable list) comprising of the two elements, x and y. So, the following two lines of code are exactly equivalent:

something = x, y
something = (x, y)

A tuple can, of course, contain more than two elements:

something = a, b, c, d, e
something = (a, b, c, d, e)

The intended use case of this feature is to make it easier/cleaner to do things like return multiple values from a function:

def foo():
    return "hello", "world"

The second feature is tuple/list unpacking. Whenever you have a series of variables on the left-hand side, and any sort of list, tuple, or other collection on the other, Python will attempt to match up each of the elements in the right to the ones on the left:

>>> a, b, c = [11, 22, 33]
>>> print(a)
11
>>> print(b)
22
>>> print(c)
33

If it helps, the line a, b, c = [11, 22, 33] is basically identical to doing:

temp = [11, 22, 33]
a = temp[0]
b = temp[1]
c = temp[2]

Note that the right-hand side can be literally any kind of collection, not just tuples or lists. So the following code is valid:

>>> p, q = "az"
>>> print(p + "  " + q)
a  z
>>>
>>> s, t = {'cat': 'foo', 'dog': 'bar'}
>>> print(s + "  " + t)
cat  dog

(Though, since dictionaries in Python are not obligated to be in any particular order, and since the order of the keys can be freely scrambled, unpacking them probably isn't going to be useful since you'd potentially get different results each time.)

If the number of variables and the number of elements in the collection do not match up, Python will throw an exception:

>>> a, b = (1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 2)

>>> a, b, c = (1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack

So that means that if we called our foo function from above and did the following:

>>> x, y = foo()

...the variable x would equal the string "hello", and the variable y would equal the string "world".


So ultimately, that means that your original snippit of code:

x = "salil"
y = "Ajay"
y, x = x, y

...is logically equivalent to the following:

x = "salil"
y = "Ajay"
temp = (x, y)  # evaluates to ("salil", "Ajay")
y, x = temp

...which broken down even more, is logically equivalent to the following:

x = "salil"
y = "Ajay"
temp = (x, y)  # evaluates to ("salil", "Ajay")
y = temp[0]
x = temp[1]

Note that you can think of these two operations take place separately. First the tuple is formed and evaluated, then the tuple is unpacked back into the variables. The net effect is that the values of your two variables are interchanged.

(However, as it turns out, the CPython interpreter (the original and 'standard' implementation of Python) does a bit of optimization here: it will optimize the swap and won't do the full tuple unpacking -- see comments below. I'm not sure if other implementations of Python do the same, though I suspect they might. In any case, this sort of optimization is implementation-specific, and is independent to the design of the Python language itself.)

Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
  • on the right side of = operator does the interpreter do packaging or assignment first ? – salil vishnu Kapur Jul 13 '15 at 03:06
  • 2
    @salilvishnuKapur The right-hand side is always fully evaluated before assignment takes place. This is always the case for Python and most programming languages), not just with tuple packing/unpacking. – Michael0x2a Jul 13 '15 at 03:08
  • but doesn't the interpreter read every line of code from left to right ? – salil vishnu Kapur Jul 13 '15 at 03:10
  • 1
    @salilvishnuKapur -- yes and no. The assignment operator has a very low precedence, so the right hand side by necessity will be evaluated first. It's an over-simplification to assume that the interpreter will always go from left to right. It will usually do so, but things like parenthesis, order of operations, etc. can force it to do otherwise + start evaluating somewhere in the middle of a line. However, once it picks a place to start evaluating, then yes, it does go from left to right. – Michael0x2a Jul 13 '15 at 03:11
  • 2
    It parses the code from left to right, but it evaluates it in a different order. In an expression `y = f(x)`, the evaluation order would be `x`, then `f`, and finally `y`. – Nayuki Jul 13 '15 at 03:11
  • 5
    There's also a little bit of optimization here. `a,b = b,a` is actually just a straight swap: `LOAD_FAST,LOAD_FAST,ROT_TWO`. You can see it, using [dis](https://docs.python.org/2/library/dis.html). – NightShadeQueen Jul 13 '15 at 03:14
  • @ Michael0x2a i guess you mean to say that evaluation is done on the basis of order of precedence of operators ? @ Nayuki Minase is parsing and compiling one and the same thing ? – salil vishnu Kapur Jul 13 '15 at 03:15
  • @NightShadeQueen -- huh, I did not know that, thanks! I'll edit my post. – Michael0x2a Jul 13 '15 at 03:17
  • 1
    @salilvishnuKapur - Yes, that's correct. And parsing is the act of looking through a string to try and understand what it means. Compiling is then the step of taking the code you parsed, and transforming it into another language. In this case, Python will parse your code, then compile it into bytecode, which it will then run. – Michael0x2a Jul 13 '15 at 03:21
  • okay , but could you explain LOAD_FAST,LOAD_FAST,ROT_TWO in a little detail, i am not getting it – salil vishnu Kapur Jul 13 '15 at 03:24
  • 3
    @salilvishnuKapur - If you're a beginner/new to programming, this isn't something you should really worry about. Basically, what's happening is that Python will take the code you write, and first convert it into a simpler, more basic programming language before running your code. In this more basic language, the swapping was compiled down to the operations LOAD_FAST, LOAD_FAST, and ROT_TWO (load two variables; rotate their values). However, the details of how Python bytecode work is not something that is going to be relevant to you on a day-to-day basis, and is something that you can ignore. – Michael0x2a Jul 13 '15 at 03:27
  • @salilvishnuKapur - Basically, it's interesting that Python chose to optimize this swapping operation at the bytecode level, but that's an implementation decision that the Python interpreter made -- there's nothing stopping somebody else from creating a different Python interpreter which does _not_ perform this operation. It's interesting + useful to know how bytecode works if you want to optimize your code, but is completely irrelevant if your goal is to learn Python + understand how variable swapping conceptually works. – Michael0x2a Jul 13 '15 at 03:29
  • okay , I am going to accept your answer for your great support.But,I would love to understand more about code optimization at bytecode level . One more question , interpreter chooses to optimize this swapping operation at the bytecode level after the compitation and tuple evaluation ? – salil vishnu Kapur Jul 13 '15 at 03:34
  • @salilvishnuKapur: it's done during the compilation step -- when deciding how to turn Python code into bytecode. – Michael0x2a Jul 13 '15 at 03:37
  • @ Michael0x2a okay thanks – salil vishnu Kapur Jul 13 '15 at 03:39
  • 1
    I wouldn't call the tuple building with `x, y` "implicit". Seems rather explicit to me... – glglgl Jul 13 '15 at 03:49
  • 2
    @glglgl -- I suppose I meant implicit in the sense that there are no parens, so to a beginner it may not be obvious that this is creating a tuple. But yes, I do agree that if you know what's going on, it's pretty explicit. – Michael0x2a Jul 13 '15 at 03:51
  • 1
    I'm being pedantic, but I feel it's incorrect to say "Python does a bit of optimization here". Like you said in a comment, @Michael0x2a, the optimization isn't part of the language, but rather part of the implementation. – flornquake Jul 13 '15 at 13:15
  • @flornquake -- fair point; edited. – Michael0x2a Jul 13 '15 at 13:24
  • @Michael0x2a: Assignment [is a statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements) in Python. There's no such thing as "the assignment operator." – Kevin Jul 13 '15 at 13:42
10

Okay, let's see:

import dis
src = '''
x="salil"
y="Ajay"
y,x=x,y

print x +" "+ y
'''
code = compile(src, '<string>', 'exec')
dis.dis(code)

This produces:

  2           0 LOAD_CONST               0 ('salil')
              3 STORE_NAME               0 (x)

  3           6 LOAD_CONST               1 ('Ajay')
              9 STORE_NAME               1 (y)

  4          12 LOAD_NAME                0 (x)
             15 LOAD_NAME                1 (y)
             18 ROT_TWO             
             19 STORE_NAME               1 (y)
             22 STORE_NAME               0 (x)

  6          25 LOAD_NAME                0 (x)
             28 LOAD_CONST               2 (' ')
             31 BINARY_ADD          
             32 LOAD_NAME                1 (y)
             35 BINARY_ADD          
             36 PRINT_ITEM          
             37 PRINT_NEWLINE       
             38 LOAD_CONST               3 (None)
             41 RETURN_VALUE        

Remember that Python operates as a stack machine. In this case, it optimized the assignment to a ROT_TWO (i.e. swap) instruction. There's also a ROT_THREE instruction. But let's try something else:

import dis
src = 'x, y, z, w = a, b, c, d'
code = compile(src, '<string>', 'exec')
dis.dis(code)

This produces the general form:

  1           0 LOAD_NAME                0 (a)
              3 LOAD_NAME                1 (b)
              6 LOAD_NAME                2 (c)
              9 LOAD_NAME                3 (d)
             12 BUILD_TUPLE              4
             15 UNPACK_SEQUENCE          4
             18 STORE_NAME               4 (x)
             21 STORE_NAME               5 (y)
             24 STORE_NAME               6 (z)
             27 STORE_NAME               7 (w)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        
o11c
  • 15,265
  • 4
  • 50
  • 75
5

This works for swapping because the right hand side of the = is evaluated first.

So on the right, it evaluates to

'salil', 'Ajay'

and then the assignment of x and y happens

 x, y = 'salil', 'Ajay'
logee
  • 5,017
  • 1
  • 26
  • 34