Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

More elegant way to initialize list of duplicated items in Python

Tags:

python

If I want a list initialized to 5 zeroes, that's very nice and easy:

[0] * 5

However if I change my code to put a more complicated data structure, like a list of zeroes:

[[0]] * 5

will not work as intended, since it'll be 10 copies of the same list. I have to do:

[[0] for i in xrange(5)]

that feels bulky and uses a variable so sometimes I even do:

[[0] for _ in "     "]

But then if i want a list of lists of zeros it gets uglier:

[[[0] for _ in "     "] for _ in "     "]

all this instead of what I want to do:

[[[0]]*5]*5

Has anyone found an elegant way to deal with this "problem"?

like image 955
Claudiu Avatar asked Jun 09 '10 19:06

Claudiu


People also ask

Can a list have duplicate values in Python?

If size of list & set is equal then it means no duplicates in list. If size of list & set are different then it means yes, there are duplicates in list.

Why set does not allow duplicates in Python?

Sets cannot contain duplicates. Duplicates are discarded when initializing a set. If adding an element to a set, and that element is already contained in the set, then the set will not change.


1 Answers

After thinking a bit about it, I came up with this solution: (7 lines without import)

# helper
def cl(n, func):
    # return a lambda, that returns a list, where func(tion) is called
    return (lambda: [func() for _ in range(n)])

def matrix(base, *ns):
    # the grid lambda (at the start it returns the base-element)
    grid = lambda: base

    # traverse reversed, to handle the midmost values first
    for n in reversed(ns):
        # assign a new lambda with the last grid within (and call it)
        grid = cl(n, grid)

    return grid() # call the full grid (but the matrix calls you ^^)

The tests give the following results:

>>> from pprint import pprint as pp
>>> 
>>> matrix(None, 2,3)
[[None, None, None], [None, None, None]]
>>> 
>>> matrix(None, 4,3)
[[None, None, None], [None, None, None], [None, None, None], [None, None, None]]
>>> 
>>> x = matrix(None, 3,5,2)
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]
>>> x[1][3][0] = "test"
>>> pp(x)
[[[None, None], [None, None], [None, None], [None, None], [None, None]],
 [[None, None], [None, None], [None, None], ['test', None], [None, None]],
 [[None, None], [None, None], [None, None], [None, None], [None, None]]]

Another solution, which has the advantage of using the "[[[0]]*5]*5"-syntax:

def uniq(base, l):
    # function used to replace all values with the base
    nl = []
    for i in l:
        if type(i) is list:
            nl.append(uniq(base, i)) # recursion for deep lists
        else:
            nl.append(base)
    return nl

Test:

# first arg is the base, the 0 inside the [] is just a dummy
# (for what None is the best choice usually)
>>> x = uniq(0, [[[0]]*5]*5)
>>> x[0][3][0] = 5
>>> pp(x)
[[[0], [0], [0], [5], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]],
 [[0], [0], [0], [0], [0]]]

btw. the numpy library has a np.zeros(s)-function, where s is a shape like (3,4,5)

>>> s = (2,2)
>>> np.zeros(s)
array([[ 0.,  0.],
       [ 0.,  0.]])

Finally a performance test:

# functions are already defined ...
import timeit
>>> # Alex Martelli's Code
>>> t1 = timeit.Timer( lambda: multi_dimension_list(None, 3,4,5) )
>>> # the two mentioned above
>>> t2 = timeit.Timer( lambda: matrix(None, 3,4,5) )
>>> t3 = timeit.Timer( lambda: uniq(None, [[[None]*5]*4]*3) )
>>> 
>>> t1.timeit(10000)
2.1910018920898438
>>> t2.timeit(10000)
0.44953203201293945
>>> t3.timeit(10000)
0.48807907104492188

I found it really interesting to discover this problem. So, thanks for the question :)

like image 84
Joschua Avatar answered Oct 14 '22 03:10

Joschua