3
a = [[0]*3] * 4
print(a[0] is a[1]) # True

when I initialize a two dimensional array that way, things went wrong. Took me a bit time to find this unexpected behavior. So is this syntax only work on immutable object?

Alex Kai
  • 31
  • 2
  • it copies the list element by reference, it is not a deep copy – anthony sottile Apr 15 '20 at 02:21
  • For mutable objects the behavior is at least unexpected for Python beginners (although it is logical that this expression can't duplicate arbitrary objects and therefore doesn't try to). – Michael Butscher Apr 15 '20 at 02:21
  • And try this also, `a[0][1] = 1` and `print(a)` note the center element in all elements are updated. – Scott Boston Apr 15 '20 at 02:28
  • You may want to check out https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly – dspencer Apr 15 '20 at 02:56

2 Answers2

3

It "worked" in your example too, in its way. This is just how the implementation of list.__mul__ interprets what you want. The list can't construct new objects, it doesn't know how to create new objects from whatever objects it happens to contain. It expands itself with new references to those objects.

You get the same behavior with immutable integers

>>> x = [0] * 3
>>> x[0] is x[1]
True

You can get the two dimensional array with

>>> a = [[0]*3 for _ in range(4)]
>>> a[0] is a[1]
False

The reason why [0] * 3 does what you want is that it is creating list that contains 3 references to the same immutable 0.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
0

What went"wrong"? It does make a two-dimensional list. (I don't know if that's a thing but I mean to say it makes the list in a form where you can convert it to a 2-D array). To convert it to a proper array, you will have to use

import numpy as np
a = [[0]*3] * 4
a = np.array(a)

You can make this immutable by converting it to a tuple.

nsrCodes
  • 625
  • 10
  • 25
  • I think this is a good answer as far as it goes. `numpy` is one way to make arrays, but there are others, including regular nested python lists. If you want to reference the cells of the array individually, numpy may not be the best choice as there needs to be python conversions of those cells along the way. – tdelaney Apr 15 '20 at 02:27
  • @tdelaney You are absolutely correct but I don't understand what the question is as he just said things went wrong. And this syntax does work, so there is no syntax error whether it is a list or a tuple. I couldn't clarify that through comments hence thought of writing this answer. – nsrCodes Apr 15 '20 at 02:31
  • In the original question, after creating the array, if OP did `a[0][0]=42` he would find that 42 is now in all of the rows: `[[42, 0, 0], [42, 0, 0], [42, 0, 0], [42, 0, 0]]` because the outer list has 4 references to a single inner list, not 4 inner lists. – tdelaney Apr 15 '20 at 02:35
  • @tdelaney I actually did not see that. But why would you need "python conversions" to reference the cells in a numpy array. Is it because numpy arrays contains elements of a particular data type? – nsrCodes Apr 15 '20 at 02:40
  • 1
    Python objects are rather bulky. An integer is something like 44 bytes (I don't remember exactly). You need to create the object, fiddle with its reference count and then store a pointer to the object in a container. In numpy, an array of integers is just a block of memory holding those integers. numpy functions use the integers directly, but python operations need to grab the integer, create a python int and then use it. Best to avoid python level references in numpy as much as possible. – tdelaney Apr 15 '20 at 02:48