0

If I do:

x = 1
y = [x,x,x,x]
y[1] = 2
print y

I get:

[1,2,1,1]

BUT if I do:

x = [1,1]
y = [x,x,x,x]
y[1][0] = 2
print y

I get:

[[2,1],[2,1],[2,1],[2,1]]

Can someone explains to me the subtle difference between the two? I mean, something like how Python allocates memory so that in the first case, the four elements of y read different memory locations, but in the 2nd case, the four elements of y read the same location?

And why does Python behave like this? As I use Matlab, nothing like this will happen.

Thank you.

maxwell
  • 121
  • 3
  • 10
  • if you want to be a list made of copies you should do like this => y = [x[:],x[:],x[:],x[:]], then you will get [[1, 1], [2, 1], [1, 1], [1, 1]] – markcial Jan 30 '14 at 22:28
  • If I do x=[1]; y=x; x[0]=2; then y is [2]. But if I do x=[1]; y=x+[1]; x[0]=2; then y does not change and is still [1,1]. So... what is the general rule? – maxwell Jan 30 '14 at 22:32
  • when you use operators you transform lists and make copies when you do `y=x+[1]` you are doing that : `import operator;y=operator.add(x,[1])` that x is a copy after the function call – markcial Jan 30 '14 at 22:36

3 Answers3

7

All variables in Python contain references (pointers). Even simple types, such as integers, which are stored directly within variables in other languages like C, are stored using references in Python. Assigning a value to a name changes what the name refers to (points to). Understanding exactly when this happens is key to understanding why Python behaves as it does.

Let's begin:

a = 2           # points a to the integer object 2
a = 3           # points a to a different integer object, 3
b = [1, 2, 3]   # points b to a new list object [1, 2, 3]

Next:

c = a            # a and c now point to the same integer object, 3
d = b            # b and d now point to the same list object, [1, 2, 3]

So far so good, right? Now you can see why this works the way it does:

d.append(4)      # b and d still point to the same list object, which is
                 # now [1, 2, 3, 4]
print(b)         # prints [1, 2, 3, 4] -- how could it not?

In truth, everything works the same way regardless of the type of the object. It's just that some types you can't change "in place:" numbers, strings, and tuples among them:

a += 2           # a now points to the integer object 5, because you can't
                 # change 2 into 5 (integers are immutable)
print(c)         # prints 3. c still points to 3, because you never told
                 # Python to make c point anywhere else!

But:

b.append(5)      # doesn't change what b points to, just changes the list
b += [6]         # also (somewhat counterintuitively) doesn't change what b
                 # points to, even though it did with integers
print(d)         # prints [1, 2, 3, 4, 5, 6] because b and d still point to
                 # the same list

The case of += is a little confusing since it behaves so differently with lists and integers. However, keep in mind that += (and most other Python operations) can be redefined by the objects themselves. In this case, += is processed by the method __iadd__() which is attached to the integer and list types. On integers, += returns a new object, because it has to, integers being immutable. On lists, it was deemed more efficient for += to return the same object it was passed, rather than making a copy.

So to sum up:

  • Python variables (names) contain references (pointers) to objects
  • Assignment changes what object a variable (name) refers to (points to)
  • Some things that look like assignment (in particular augmented assignments like +=) aren't actually assignments, but method calls, and don't necessarily change what object a variable (name) refers to (points to), though they can
kindall
  • 178,883
  • 35
  • 278
  • 309
  • Thank you, although the whole looks like a mess of Python. Could you be kind enough to tell me why Python chooses to behave like this? Using pointers saves resources, perhaps? – maxwell Jan 30 '14 at 23:08
  • The better question is why do other languages behave in such inconsistent ways? Why does C pass arrays by reference but integers by value? The reason C does it like that is performance. In Python it makes a lot of things simpler for everything to be a reference, and performance (while good) is not the primary goal. – kindall Jan 30 '14 at 23:14
0

In first case x is just an integer while in second case, it is a reference to list. The assignment in first case, will copy the content of x into list which is an integer. After the assignment, any changes made to x will not be reflected in y because x and elements of y are stored in different memory locations.

But in second case the assignment y=[x,x,x,x] is storing 4 references of same list [1,1]. As every reference is pointing to the same memory location, changing any one of them or even changing the original x will reflect the same change everywhere.

santosh-patil
  • 1,540
  • 1
  • 15
  • 29
0

The problem is that in the first example you are changing the content of y and on the second you are changing the content of a list inside y

Try this:

x = 1
y = [x,x,x,x]
print map(id,y)

x = [1,1]
y = [x,x,x,x]
print map(id,y)

Output(it will vary):

[41548904, 41548904, 41548904, 41548904]
[50255344, 50255344, 50255344, 50255344]

Note that both list have 4 times the same object, now try:

x = 1
y = [x,x,x,x]
y[1] = 2
print map(id,y)

x = [1,1]
y = [x,x,x,x]
y[1] = [2,1]
print map(id,y)

Output(will vary):

[28224616, 28224592, 28224616, 28224616]
[36931056, 36929264, 36931056, 36931056]

Note that you have changed in both examples an element of the list y.

Finally if you do:

x = [1,1]
y = [x,x,x,x]
y[1][0] = 2

You are changing the content of the element on the position 1 which is x (as in all others positions) so when you print y you will print x four times with its first value changed.

Alvaro Fuentes
  • 16,937
  • 4
  • 56
  • 68
  • Suppose x is a list. If I do id(x) I get the pointer id, but if I do map(id,x) I get the id's of the stuff that x points to. Am I right? – maxwell Jan 30 '14 at 22:58
  • `id` returns the internal id of the python object which is unique on every _live_ object. Read [this](http://docs.python.org/2/library/functions.html#id). – Alvaro Fuentes Jan 30 '14 at 23:08