0

So I made this class which has only one nested list. According to me [[2,2],[3,3],[4,3]] should be the output. That is how item assignment works in python right. Here's the code:

class A():
    def __init__(self):
        self.a = [[1,1],[2,2],[3,3]]
        self.start()

    def start(self):
        self.do()
        self.a[2][0] += 1
        print(self.a)

    def do(self):
        self.a[0] = self.a[1]
        self.a[1] = self.a[2]

aaa = A()

But the output came out to be [[2,2],[4,3],[4,3]]. With the last list, somehow the second list also changed. And this happened in line 8 of the code.

Please tell me what is wrong, the output or my python knowledge?

Pratyush
  • 13
  • 1
  • Classic problem in nested python lists, reference vs value. A possible workaround : `self.a[1] = self.a[2][:]` – david Mar 30 '20 at 12:50
  • see for example https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list. – Heike Mar 30 '20 at 12:50
  • Does this answer your question? [How to clone or copy a list?](https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list) – Ch3steR Mar 30 '20 at 12:58

1 Answers1

0

I'll define a = [[1,1],[2,2],[3,3]] a bit differently. Let's say:

b = [1,1]
c = [2,2]
d = [3,3]
a = [b,c,d]

This is the exact same thing as you have, we just gave the inner list names so that it is easier to follow later on.

One might call those names references, or pointers to memory locations. If you're not familiar with those terms, a way of looking at is "b does not contain [1,1], it just knows where it is in the program's memory".

 

Ok, now at self.a[0] = self.a[1]. What this line does is it says to the program: "assign the first element of list a to be the same as the second one". It does not say "copy the second element to the first one". The difference is that both self.a[0] and self.a[1] point to the same array [2,2]. In other words a = [c,c,d] now.

self.a[1] = self.a[2] does the same thing, but for different elements, so a = [c, d, d]. So far, everything is as you expect it. The array equals [[2,2],[3,3],[3,3]] when you print it, but the problem is that the [3,3] here is the same array, it can just be accessed by two elements of self.a.

Now let's tackle the self.a[2][0] += 1 part step by step:

  1. by calling self.a[2] we access the pointer to the array d = [3,3]
  2. following that logic, saying self.a[2][0] is the same as saying d[0]
  3. since we have incremented d[0], and both self.a[1] and self[2] point to d, both self.a[1][0] and self.a[2][0] have changed.

Another way of looking at it is that your code is equivalent to:

class A():
    def __init__(self):
        self.b = [1,1]
        self.c = [2,2]
        self.d = [3,3]
        self.a = [self.b, self.c, self.d]
        self.a[0] = self.c
        self.a[1] = self.d
        self.d[0] += 1
        print(self.a)

aaa = A()

The array in self.a is not an array that contains arrays, it's just an array that contains variables that know where those arrays are (i.e. pointers or references).

5ar
  • 2,069
  • 10
  • 27