1

I need to initialize 2d array with list.
For example 2X3 array: [[0,0,0], [0,0,0]]

First attempt:

In: a1 = [[0]*3]*2
In: a1[0][0] = 100
In: a1
Out: [[100,0,0], [100,0,0]]

This is strange. So I checked:

In: a1 = [[0]*3]*2
In: id(a1[0][0])
Out: 4518461984
In: id(a1[1][0])
Out: 4518461984

Same address.

Second attempt:

In: a2 =[[0]*3 for i in range(2)]
In: a2[0][0] = 100
In: a2
Out: [[100, 0, 0], [0, 0, 0]]

Right.

Let me check memory address again:

In: a2 =[[0]*3 for i in range(2)]
In: id(a2[0][0])
Out: 4518461984
In: id(a2[1][0])
Out: 4518461984

Well, strange. Same address again. I expected different addresses. My initial guess is that the address returned is the address of pointer to value. Then how can I retrieve the address of the slot?

Is there anyone who can explain the workings of Python that caused this behavior? In Python, I think it's very hard to know which is pointer and which is value.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
Hee Kyung Yoon
  • 253
  • 1
  • 4
  • 14
  • I've read it, but it's different because, it is about when copy is generated depending on using * or range. – Hee Kyung Yoon Jun 21 '17 at 07:12
  • This comment by @Coldspeed solved my question. `As an added note, beware that, if using * with mutable structures, those references are reused as well. – Coldspeed` – Hee Kyung Yoon Jun 21 '17 at 07:13

2 Answers2

2

You're comparing the memory address of the values in the lists not the addresses of your lists.

The memory addresses of your lists differ:

>>> a2 =[[0]*3 for i in range(2)]
>>> id(a2[0]) == id(a2[1])   # compare memory addresses of the sublists
False

But in your first example the "sub" lists are identical:

>>> a1 = [[0]*3]*2       
>>> id(a1[0]) == id(a1[1])
True

The question why the values have the same memory adress is more complicated:

  • Python reuses the integers -5 to 255 (CPython at least) so 0 will always have the same memory address.

    >>> a = 0
    >>> b = 0
    >>> a is b  
    True
    
  • literal numbers like 10000 have the same memory address if they are defined in the same block (used in the same function - maybe also same module - or in the same "line" when not in a function).

    >>> a = 5000
    >>> b = 5000
    >>> a is b    # different "lines" and not in a function!
    False
    
    >>> a, b = 5000, 5000
    >>> a is b    # defined on the same "line"
    True
    
  • When you multiply a list the references are reused. In this case it doesn't matter because it's a literal number, so the reference is always reused. But in case it's not a literal number that may be important:

    >>> l = [int('1000')]*3
    >>> l[0] is l[1]
    True
    
    >>> l = [int('1000') for _ in range(3)]
    >>> l[0] is l[1]
    False
    

In your case you define the number in the same line and it's a small integers so they will always have the same memory address.

MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 2
    As an added note, beware that, if using * with mutable structures, those references are reused as well. – cs95 Jun 21 '17 at 07:03
  • @Coldspeed That's correct, but that only affects each sublist not if you compare different sublists. The reason why the first number in the first sublist has the same id as the first number in the second sublist is because of the "small integer cache". – MSeifert Jun 21 '17 at 07:06
1

In Python the expression [v] * n is equivalent to "append a reference to v to the outer list n times".

This is always fine for f being a literal:

a = [1] * 4
id(a[0]) == id(a[2])    # True

a[0] = 15                
print(a)                # a == [15, 1, 1, 1]

However, the same mechanism applies for f being a mutable. A reference to f is inserted n times.

a = [ [1, 2, 3] ] * 3

If you then modify any element of a, all the others will also be modified, since they are all references to the same list.

a[0][0] = 2
print(a)        # [[2, 2, 3], [2, 2, 3], [2, 2, 3]]

You can learn more about this behavior by reading the docs on common sequence operations.

The suggested way of creating a multidimensional list is:

n = 5
mda = [[0] * 3 for _ in range(5)]

This works, because in each iteration of the loop a new list instance is created and then appended to the outer list.

MaxPowers
  • 5,235
  • 2
  • 44
  • 69