Reputation: 23
Suppose I want to make an empty 3x4 2d array like such.
x = [[0.0]*3]*4
However, using the above code,
print x[0] is x[1] # Output = True
Meaning,
x[1][1] = 5.0
print x # Output = [[0.0, 5.0, 0.0],
# [0.0, 5.0, 0.0],
# [0.0, 5.0, 0.0],
# [0.0, 5.0, 0.0]]
To bypass creating this list with 4 identical references to the same list, I've been doing things like:
y = [[0.0]*3, [0.0]*3, [0.0]*3, [0.0]*3]
Where
print y[0] is y[1] # Output = False
y[1][1] = 5.0
print y # Output = [[0.0, 0.0, 0.0],
# [0.0, 5.0, 0.0],
# [0.0, 0.0, 0.0],
# [0.0, 0.0, 0.0]]
Another approach would be to use list comprehension
z = [[0.0]*3 for x in range(4)]
but, this still seems a little ugly.
Is there a way to make the array 'y' or 'z' where all the references are unique in an elegant format such as in 'x' using multiply on a list?
Upvotes: 2
Views: 142
Reputation: 366133
This is in the official Python Programming FAQ, as How do I create a multidimensional list?.
The FAQ suggests three possibilities:
I think most Python developers would suggest these in the exact opposite order as they're listed in the FAQ… but nobody would complain about any of them. So:
a = np.zeros((3, 4))
a = [[0.0]*3 for _ in range(4)]
a = [None] * 4
for i, _ in enumerate(a):
a[i] = [0.0]*3
If you find yourself doing this a lot (and not using numpy), you can always wrap it in a function. In fact, this is generally the best way to deal with anything that looks ugly even when you're doing it as pythonically as possible. So:
def make_matrix(r, c):
return [[0.0]*c for _ in range(r)]
And then it's about as clear as can be:
a = make_matrix(3, 4)
itertools
has a function called repeat
, which is equivalent to the iterator version of *
. The docs show some equivalent pure Python code, so it's pretty easy to adapt it to (shallow or deep) copy the object for each rep:
def repeat(obj, times):
for _ in range(times):
yield copy.copy(obj)
list(repeat([0.0]*3, 4))
I don't think this is more readable—especially if you want to use it at multiple levels:
list(repeat(list(repeat(0.0, 3)), 4))
… even if you wrap that in a function that listifies for you:
def srepeat(object, times):
return list(repeat(object, times))
srepeat(srepeat(0.0, 3), 4)
… and if you wrap it in a function that handles multiple dimensions, you've just re-created make_matrix
.
Upvotes: 3
Reputation: 282026
List comprehensions are the usual answer. Not as clean as sequence multiplication, but the best that's available:
[[0.0]*3 for _ in xrange(4)]
Note that for a lot of applications where you want a grid of values, you're doing something that numpy ndarrays could do faster and cleaner.
Upvotes: 4