2010-06-09 2 views
10

Si je veux une liste initialisée à 5 zéros, ce qui est très agréable et facile:plus élégante façon d'initialiser la liste des éléments dupliqués en Python

[0] * 5 

Cependant, si je change mon code pour mettre une structure de données plus complexe , comme une liste de zéros:

[[0]] * 5 

ne fonctionnera pas comme prévu, car il sera 10 exemplaires de la même liste. Je dois faire:

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

qui se sent volumineux et utilise une variable si parfois je ne même:

[[0] for _ in "  "] 

Mais si je veux une liste de listes de zéros, il obtient plus laid:

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

tout cela au lieu de ce que je veux faire:

[[[0]]*5]*5 

Est-ce que quelqu'un a trouvé une manière élégante de faire face à ce "problème"?

Répondre

9

Après avoir réfléchi un peu à ce sujet, je suis venu avec cette solution: (7 lignes sans importation)

# 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 ^^) 

Les tests donnent les résultats suivants:

>>> 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]]] 

Une autre solution, qui a l'avantage d'utiliser la syntaxe "[[[0]] * 5] * 5":

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. la bibliothèque numpy a une np.zeros(s) -fonction, où s est une forme comme (3,4,5)

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

Enfin un test de performance:

# 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 

je l'ai trouvé vraiment intéressant de découvrir ce problème. Donc, merci pour la question :)

+1

oo mignonne 15charararar – Claudiu

+0

Claudiu, j'ai ajouté une nouvelle méthode et quelques détails – Joschua

+0

pourquoi n'acceptez-vous pas une de ces réponses? – Joschua

0

Une solution est d'avoir une fonction d'assistance:

import copy 
def r(i,n): 
    return [copy.deepcopy(i) for _ in xrange(n)] 

alors:

r([0],5) 
r(r([0],5),5) 

Mais cette syntaxe est laid.

2

Une autre est d'étendre la classe de liste:

import copy 
class mlist(list): 
    def __mul__(self, n): 
    res = mlist() 
    for _ in xrange(n): 
     for l in self: 
    res.append(copy.deepcopy(l)) 
    return res 

alors:

>>> hey = mlist([mlist([0])]) 
>>> hey 
[[0]] 
>>> hey * 4 
[[0], [0], [0], [0]] 
>>> blah = hey * 4 
>>> blah[0][0] = 9 
>>> blah 
[[9], [0], [0], [0]] 

mais l'initialisation mlist est ennuyeux.

5

Si j'avais une exigence fréquente pour les listes de listes de listes de ... Je voudrais simplement emballer le bâtiment de celui-ci dans une petite fonction d'usine, tels que:

import copy 

def multi_dimension_list(baseitem, *dimensions): 
    dimensions = list(dimensions) 
    result = [baseitem] * dimensions.pop(-1) 
    for d in reversed(dimensions): 
    result = [copy.deepcopy(result) for _ in range(d)] 
    return result 

eg = multi_dimension_list(0, 3, 4, 5) 
print(eg) 
# and just to prove the parts are independent...: 
eg[1][1][1] = 23 
print(eg) 

Dans la pratique, je ne » Je ne m'inquiète même pas, parce que mes utilisations de listes multidimensionnelles de ce genre sont rares, de sorte que la compréhension des listes en ligne est parfaite. Cependant, l'idée générale de construire votre propre module de petites fonctions d'utilité pour les tâches simples que vous devez effectuer souvent et (à votre avis) ne sont pas faites élégamment par des idiomes en ligne, est vraiment la seule façon d'y aller!-)

+0

Je cours cette fonction et rencontre un problème. L'opération d'affectation eg [1] [1] [1] ne doit changer que l'élément est la position par exemple [1] [1] [1]. Mais je trouve que cela change la valeur de trois éléments. Il est câblé ... – chnet

+0

Droit - il a besoin d'une copie en profondeur (édition à corriger). –

+0

Les seules fois où j'ai eu besoin de listes multidimensionnelles, c'était lorsque je voulais des matrices. Donc, j'ai juste utilisé numpy à la place. –

Questions connexes