Python - how to inspect / discover list has references? - python

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.

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

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.

Related

Initialise a diagonal matrix in python

i'd like create in python a matrix with all zeros except for some value selected from a list. For example (very stupid) from
l=[0,1,2,3]
i'd like create a matrix (a list of list) with the letter "x" in position l[0] l[1] etc.. like this:
[x, 0, 0, 0]
[0, x, 0, 0]
[0, 0, x, 0]
[0, 0, 0, x]
i'd like make it interactive, with a variable length (not always 4) maybe giving on input
You should use numpy's diag function.
import numpy as np
np.diag(l)
array([[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3]])
With pure python, initialise an empty 2D list and populate the diagonal after.
diag = [[0] * len(l) for _ in range(len(l))]
for i, e in enumerate(l):
diag[i][i] = e
diag
# [[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]
If I understood you correctly, you want non-zero values to be placed based on the list. So, maybe something like this:
val = 'x'
l = [2,1,0,3]
matrix = [[val if i == e else 0 for i in range(len(l))] for e in l]
print(matrix)
Output will be like this:
[[0, 0, 'x', 0], [0, 'x', 0, 0], ['x', 0, 0, 0], [0, 0, 0, 'x']]

longest common subsequence matrix difference python

I'm working on a dynamic programming problem (longest common subsequence)
My issue: building the matrix.
I initially build my matrix with dp1. but it kept churning out the wrong answers. Then I referenced other answers and used dp2, which produces the right answer.
For example:
s1 = ELGGYJWKTDHLXJRBJLRYEJWVSUFZKYHOIKBGTVUTTOCGMLEXWDSXEBKRZTQUVCJNGKKRMUUBACVOEQKBFFYBUQEMYNENKYYGUZSP
s2 = FRVIFOVJYQLVZMFBNRUTIYFBMFFFRZVBYINXLDDSVMPWSQGJZYTKMZIPEGMVOUQBKYEWEYVOLSHCMHPAZYTENRNONTJWDANAMFRX
The right answer should be 27.
dp1 gives 30
dp2 gives 27
I'm puzzled. What's the difference? isn't "for _ in range(m+1)" essentially iterating whatever is before by m+1 times? please help me out.
def commonChild(s1, s2):
n, m = len(s1), len(s2)
dp1 = [[0] * (n+1)] * (m+1)
dp2 = [[0] * (n+1) for _ in range(m+1)]
for i in range(m):
for j in range(n):
if s2[i] == s1[j]:
dp[i+1][j+1] = dp[i][j] +1
else:
dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j])
return dp[-1][-1]
>>> a=[[0] * (5) for i in range(4)]
>>> a
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> a[0][0]=1
>>> a
[[1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> a=[[0] * (5) ]*4
>>> a[0][0]=1
>>> a
[[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]]
You can see the difference yourself,
in [[0]*(n+1)]*(m+1) it is referring to the same array [0] * (n+1) so changing one array value changes the same value in all

Multiplying a list by a number creates items have relation with the original one? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
I have a list of List say mysolution:
>>>mySolution
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> mySolution[0][0] = 1
>>> mySolution
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
Intended output:
[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
why is it that all the 1st elements in my list of list's is being changed to 1?
I would only like to change the first element of the first list to 1.
What matters is how you created your original mysolution list. As it seems, it contains four times the same list which is why changing it once will make it change in all four locations.
To initialize independent zero-filled lists like that, you can do the following:
mysolution = [[0] * 4 for i in range(4)]
It's quite possible that you created the list like this:
mySolution = [0]*4
mySolution = [mySolution]*4
Or equivalently:
mySolution = [[0]*4]*4
Either of the above snippets will create a list with four sublists which are copies of the exact, same sublist, so any modification on one sublist will be reflected on the others - they're one and the same. The solution is to create four different sublists:
mySolution = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Or a bit shorter:
mySolution = [[0]*4 for _ in xrange(4)]
Because all the contained lists are actually the same list. When you do:
l = [0, 0, 0, 0]
my_solution = [l, l, l]
Then, my_solution[0], my_solution[1], and my_solution[2] are references to the same object (l).
If you modify the list in one location, it changes everywhere. That is because lists are mutable objects.
Instead, use multiple lists:
l1 = [0, 0, 0, 0]
l2 = [0, 0, 0, 0]
l3 = [0, 0, 0, 0]
my_solution = [l1, l2, l3]
Which will work as intended.
please note that this is doing fine:
mySolution = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
mySolution[0][0] = 1
print mySolution
>>>
[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
it all depends on how you initialized your solution. this
mySolution = [[0, 0, 0, 0]]*4
mySolution[0][0] = 1
print mySolution
gives
>>>
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
>>>
because here each array [0, 0, 0, 0] in mySolution is a copy of initialization array [0, 0, 0, 0] in [[0, 0, 0, 0]]*4. if you change first element of first array, copy of it also change.
with this initialization mySolution = [[0, 0, 0, 0] for x in range(4)] you are not copying the array but appending [0,0,0,0] four times, giving the result that you are expecting.

Changing an element in one list changes multiple lists [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
I have a list of List say mysolution:
>>>mySolution
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> mySolution[0][0] = 1
>>> mySolution
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
Intended output:
[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
why is it that all the 1st elements in my list of list's is being changed to 1?
I would only like to change the first element of the first list to 1.
What matters is how you created your original mysolution list. As it seems, it contains four times the same list which is why changing it once will make it change in all four locations.
To initialize independent zero-filled lists like that, you can do the following:
mysolution = [[0] * 4 for i in range(4)]
It's quite possible that you created the list like this:
mySolution = [0]*4
mySolution = [mySolution]*4
Or equivalently:
mySolution = [[0]*4]*4
Either of the above snippets will create a list with four sublists which are copies of the exact, same sublist, so any modification on one sublist will be reflected on the others - they're one and the same. The solution is to create four different sublists:
mySolution = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Or a bit shorter:
mySolution = [[0]*4 for _ in xrange(4)]
Because all the contained lists are actually the same list. When you do:
l = [0, 0, 0, 0]
my_solution = [l, l, l]
Then, my_solution[0], my_solution[1], and my_solution[2] are references to the same object (l).
If you modify the list in one location, it changes everywhere. That is because lists are mutable objects.
Instead, use multiple lists:
l1 = [0, 0, 0, 0]
l2 = [0, 0, 0, 0]
l3 = [0, 0, 0, 0]
my_solution = [l1, l2, l3]
Which will work as intended.
please note that this is doing fine:
mySolution = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
mySolution[0][0] = 1
print mySolution
>>>
[[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
it all depends on how you initialized your solution. this
mySolution = [[0, 0, 0, 0]]*4
mySolution[0][0] = 1
print mySolution
gives
>>>
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
>>>
because here each array [0, 0, 0, 0] in mySolution is a copy of initialization array [0, 0, 0, 0] in [[0, 0, 0, 0]]*4. if you change first element of first array, copy of it also change.
with this initialization mySolution = [[0, 0, 0, 0] for x in range(4)] you are not copying the array but appending [0,0,0,0] four times, giving the result that you are expecting.

Python 2.7 creating a multidimensional list

In Python I want an intuitive way to create a 3 dimensional list.
I want an (n by n) list. So for n = 4 it should be:
x = [[[],[],[],[]],[[],[],[],[]],[[],[],[],[]],[[],[],[],[]]]
I've tried using:
y = [n*[n*[]]]
y = [[[]]* n for i in range(n)]
Which both appear to be creating copies of a reference.
I've also tried naive application of the list builder with little success:
y = [[[]* n for i in range(n)]* n for i in range(n)]
y = [[[]* n for i in range(1)]* n for i in range(n)]
I've also tried building up the array iteratively using loops, with no success. I also tried this:
y = []
for i in range(0,n):
y.append([[]*n for i in range(n)])
Is there an easier or more intuitive way of doing this?
I think your list comprehension versions were very close to working. You don't need to do any list multiplication (which doesn't work with empty lists anyway). Here's a working version:
>>> y = [[[] for i in range(n)] for i in range(n)]
>>> print y
[[[], [], [], []], [[], [], [], []], [[], [], [], []], [[], [], [], []]]
looks like the most easiest way is as follows:
def create_empty_array_of_shape(shape):
if shape: return [create_empty_array_of_shape(shape[1:]) for i in xrange(shape[0])]
it's work for me
i found this:
Matrix = [[0 for x in xrange(5)] for x in xrange(5)]
You can now add items to the list:
Matrix[0][0] = 1
Matrix[4][0] = 5
print Matrix[0][0] # prints 1
print Matrix[4][0] # prints 5
from here: How to define two-dimensional array in python
A very simple and elegant way is:
a = [([0] * 5) for i in range(5)]
a
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
In Python I made a little factory method to create a list of variable dimensions and variable sizes on each of those dimensions:
def create_n_dimensional_matrix(self, n):
dimensions = len(n)
if (dimensions == 1):
return [0 for i in range(n[0])]
if (dimensions == 2):
return [[0 for i in range(n[0])] for j in range(n[1])]
if (dimensions == 3):
return [[[0 for i in range(n[0])] for j in range(n[1])] for k in range(n[2])]
if (dimensions == 4):
return [[[[0 for i in range(n[0])] for j in range(n[1])] for k in range(n[2])] for l in range(n[3])]
run it like this:
print(str(k.create_n_dimensional_matrix([2,3])))
print(str(k.create_n_dimensional_matrix([3,2])))
print(str(k.create_n_dimensional_matrix([1,2,3])))
print(str(k.create_n_dimensional_matrix([3,2,1])))
print(str(k.create_n_dimensional_matrix([2,3,4,5])))
print(str(k.create_n_dimensional_matrix([5,4,3,2])))
Which prints:
The two dimensional lists (2x3), (3x2)
The three dimensional lists (1x2x3),(3x2x1)
The four dimensional lists (2x3x4x5),(5x4x3x2)
[[0, 0], [0, 0], [0, 0]]
[[0, 0, 0], [0, 0, 0]]
[[[0], [0]], [[0], [0]], [[0], [0]]]
[[[0, 0, 0], [0, 0, 0]]]
[[[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]], [[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]], [[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]], [[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]], [[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]]]
[[[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]], [[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]]]
How about this:
class MultiDimList(object):
def __init__(self, shape):
self.shape = shape
self.L = self._createMultiDimList(shape)
def get(self, ind):
if(len(ind) != len(self.shape)): raise IndexError()
return self._get(self.L, ind)
def set(self, ind, val):
if(len(ind) != len(self.shape)): raise IndexError()
return self._set(self.L, ind, val)
def _get(self, L, ind):
return self._get(L[ind[0]], ind[1:]) if len(ind) > 1 else L[ind[0]]
def _set(self, L, ind, val):
if(len(ind) > 1):
self._set(L[ind[0]], ind[1:], val)
else:
L[ind[0]] = val
def _createMultiDimList(self, shape):
return [self._createMultiDimList(shape[1:]) if len(shape) > 1 else None for _ in range(shape[0])]
def __repr__(self):
return repr(self.L)
You can then use it as follows
L = MultiDimList((3,4,5)) # creates a 3x4x5 list
L.set((0,0,0), 1)
L.get((0,0,0))
import copy
dimensions = 2, 3, 4
z = 0
genList = lambda size,value: [copy.deepcopy(value) for i in range(size)]
for i in dimensions: z = genList(i, z)
I am amazed no one tried to devise a generic way to do it.
See my answer here: https://stackoverflow.com/a/33460217/5256940
import copy
def ndlist(init, *args): # python 2 doesn't have kwarg after *args
dp = init
for x in reversed(args):
dp = [copy.deepcopy(dp) for _ in xrange(x)] # Python 2 xrange
return dp
l = ndlist(0, 1, 2, 3, 4) # 4 dimensional list initialized with 0's
l[0][1][2][3] = 1
Edit: Built on user2114402's answer: added default value parameter
def ndlist(s, v):
return [ndlist(s[1:], v) for i in xrange(s[0])] if s else v
Here's one that will give you an N dimensional "matrix" filled up with copies of a copiable object.
Edit: This is a slight modification of pterodragon's original answer, which I much prefer to user2114402's less readable answer. In fact, outside of a doc-string the only difference from pterodragon's solution is that I explicitly use a list of dimension sizes, rather than have the user pass them as arguments.
import copy
def instantiate_mdl(dim_maxes, base=0):
""" Instantiate multi-dimensional list, that is a list of list of list ...
Arguments:
dim_maxes (list[int]): a list of dimension sizes, for example
[2, 4] represents a matrix (represented by lists) of 2 rows and
4 columns.
base (object): an optional argument indicating the object copies
of which will reside at the lowest level in the datastructure.
Returns:
base (list[base]): a multi-dimensional list of lists structure,
which is filled with clones of the base parameter.
"""
for dim_max in reversed(dim_maxes):
base = [copy.deepcopy(base) for i in range(dim_max)]
return base
data = instantiate_mdl([3, 5])
data[0][0] = 99999
data[1][1] = 88888
data[2][4] = 77777
for r in data:
print(r)
>>> # Output
>>> [99999, 0, 0, 0, 0]
>>> [0, 88888, 0, 0, 0]
>>> [0, 0, 0, 0, 77777]
Here is a losution that works for any number of dimention :
def multi_dimensional_list(dimensions, filling=None):
if len(dimensions) == 1:
return [filling] * dimensions[0]
else:
return [
multi_dimensional_list(dimensions[1:], filling)
for _ in range(dimensions[0])
]
print(multi_dimensional_list([2, 3, 4], 0))
"""
output :
[
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
]
"""
Here is a more generic way of doing it.
def ndlist(shape, dtype=list):
t = '%s for v%d in xrange(shape[%d])'
cmd = [t % ('%s', i + 1, i) for i in xrange(len(shape))]
cmd[-1] = cmd[-1] % str(dtype())
for i in range(len(cmd) - 1)[::-1]:
cmd[i] = cmd[i] % ('[' + cmd[i + 1] + ']')
return eval('[' + cmd[0] + ']')
list_4d = ndlist((2, 3, 4))
list_3d_int = ndlist((2, 3, 4), dtype=int)
print list_4d
print list_3d_int
Result:
[[[[], [], [], []], [[], [], [], []], [[], [], [], []]], [[[], [], [], []], [[], [], [], []], [[], [], [], []]]]
[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]
You can also build a 2D list with different length rows using the append method.
e.g.
sf_gcov_cell = []
sf_gcov_cell.append(['r1_c1', 'r2_c2_', 'r3_c3__', 'r4_c4'])
sf_gcov_cell.append(['r2_c1', 'r2_c2'])
sf_gcov_cell.append(['r3_c1', 'r3_c2___', 'r3_c3_'])
print(sf_gcov_cell)
Result:
[['r1_c1', 'r2_c2_', 'r3_c3__', 'r4_c4'], ['r2_c1', 'r2_c2'], ['r3_c1', 'r3_c2___', 'r3_c3_']]
Just use a simple recursive function that continuously appends new arrays to the initialized arrays within itself.
# Counts the maximum amount of dimensions in an array
def dcounter(array: list, total = 1):
for arr in array:
return dcounter(arr, total + 1)
return total
# Create array with specified amount of dimensions
def nd_array(dimensions = 1, array = list(), i = 0):
if dimensions > 1:
array.append([])
nd_array(dimensions - 1, array[i])
return array
# Create a 5D array
array = nd_array(5)
print(array) # => "[[[[[]]]]]"
# Get the maximum amount of dimensions in the array
amount = dcounter(array)
print(amount) # => "5"
If you wanted to specify the size of each dimension, the amount of arrays within each dimension, then you could modify nd_array into doing so.

Categories