1

One of the more confusing aspects in python is instantiating a list of lists (assuming one isn't using numpy) - for instance, if one tries to do it via simpler multiplication you end up with reference copies:

In [1]: a = [[0] * 4] * 4
In [2]: a
Out[2]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

In [3]: a[0][1] = 1
In [4]: a
Out[4]: [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]

As mentioned in various other SO post such as this one, a proper way to instantiate without any references would be as follows:

In [5]: b = [[0 for i in range(4)] for i in range(4)]
In [6]: b         
Out[6]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

In [7]: b[0][1] = 1
In [8]: b                                           
Out[8]: [[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

The question is this - assume one does define their list as done with list a, is there a way to inspect the array in such a way that it will show that it is using references? Merely printing the array will not reveal the references.

timgeb
  • 76,762
  • 20
  • 123
  • 145
Vivek Gani
  • 1,283
  • 14
  • 28

2 Answers2

1

You can use the id function:

>>> a = [[0] * 4] * 4
>>> a
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> [id(sublist) for sublist in a]
[1975671202696, 1975671202696, 1975671202696, 1975671202696]
>>> b = [[0 for i in range(4)] for i in range(4)]
>>> b
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> [id(sublist) for sublist in b]
[1975671204808, 1975671205128, 1975671205000, 1975671204872]

As you can see, in a, the ids are all the same, while in b, they are different.

iz_
  • 15,923
  • 3
  • 25
  • 40
1

Some terminology first: you are talking about lists here (not arrays), which always store references to their elements.

A quick way to check whether all references in a list refer to different objects is

>>> l1 = [[0, 1], [0, 1]]
>>> l2 = [[0, 1]]*2
>>> 
>>> len(set(map(id, l1))) == len(l1) # no duplicates
True
>>> len(set(map(id, l2))) == len(l2) # duplicates
False

which simply checks whether there are n unique ids for the objects in a list of length n.

If your list has a very large number of elements, it might be more efficient to do this lazily and return False on the first duplicate id.

def all_unique(lst):
    seen = set()
    for x in lst:
        id_ = id(x)
        if id_ in seen:
            return False
        seen.add(id_)
    return True

... works like this:

>>> all_unique(l1)
True
>>> all_unique(l2)
False
timgeb
  • 76,762
  • 20
  • 123
  • 145