web-dev-qa-db-fra.com

Comment créer une liste ou un tuple de listes vides en Python?

Je dois remplir progressivement une liste ou un tuple de listes. Quelque chose qui ressemble à ceci:

result = []
firstTime = True
for i in range(x):
    for j in someListOfElements:
        if firstTime:
            result.append([f(j)])
        else:
            result[i].append(j)

Afin de le rendre moins bavard et plus élégant, je pensais préallouer une liste de listes vides

result = createListOfEmptyLists(x)
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)

La partie préallocation ne m'est pas évidente. Quand je fais result = [[]] * x, je reçois une liste de x références à la même liste, de sorte que la sortie de ce qui suit

result[0].append(10)
print result

est:

[[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]

Je peux utiliser une boucle (result = [[] for i in range(x)]), mais je me demande s'il existe une solution "sans boucle".

Est-ce la seule façon d'obtenir ce que je cherche 

38
Boris Gorelik
result = [list(someListOfElements) for _ in xrange(x)]

Cela fera x listes distinctes, chacune avec une copie de la liste someListOfElements (chaque élément de cette liste est par référence, mais la liste en est une copie).

Si cela vous semble plus logique, envisagez d'utiliser copy.deepcopy(someListOfElements)

Les générateurs et les compréhensions de liste et les choses sont considérées assez Pythonic .

51
Will

Il n'y a pas vraiment de moyen de créer une telle liste sans une boucle de quelque sorte. Il y a plusieurs façons de cacher la boucle, cependant, tout comme [[]] * x cache la boucle. Il y a la compréhension de la liste, qui "cache" la boucle dans une expression (mais c'est heureusement toujours évident.) Il y a aussi map(list, [[]]*x) qui a deux boucles cachées (celle dans [[]] * x et celle dans map qui crée une copie de chaque liste en utilisant list().)

Il est également possible de ne pas créer la liste à l’avance. Les autres réponses couvrent déjà l'approche simple, mais si cela ne correspond pas à vos besoins, il existe d'autres moyens. Par exemple, vous pouvez créer une fonction qui ajoute une liste vide à la liste result si nécessaire, et appeler cela:

def append(L, idx, item):
    while len(L) <= idx:
        L.append([])
    L[idx].append(item)

for i in range(x):
    for j in someListOfElements:
        append(result, i, j)

Ou vous pouvez utiliser un collections.defaultdict(list) au lieu d'une liste:

import collections
result = collections.defaultdict(list)
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)

Cela présente l'avantage d'utiliser un type déjà existant, ce qui représente moins de travail, mais cela signifie que vous avez maintenant un dict (indexé par des entiers) au lieu d'une liste, ce qui peut être ou ne pas être ce que vous voulez. Vous pouvez également créer une classe qui se comporte presque comme une liste mais qui ajoute de nouvelles listes à elle-même au lieu de générer IndexError, par exemple:

import UserList
class defaultlist(UserList.UserList):
    def __getitem__(self, idx):
        while len(self) <= idx:
            self.append([])
        return UserList.UserList.__getitem__(self, idx)

result = defaultlist()
for i in range(x):
    for j in someListOfElements:
        result[i].append(j)
7
Thomas Wouters

Vous pourriez écrire une fonction de générateur rapide. Cela aurait des utilisations autres que ce cas particulier, alors je vais le généraliser un peu. Creusez ceci:

def create(n, constructor=list):
    for _ in xrange(n):
        yield constructor()

Ensuite, pour faire une liste de listes,

result = list(create(10))

faire une liste de dicts vides,

result = list(create(20, dict))

et (par souci d’exhaustivité) de dresser une liste des Foos vides,

result = list(create(30, Foo))

Bien sûr, vous pouvez aussi faire un tuple de ce qui précède. Il ne serait pas trop difficile de l'étendre pour permettre aux arguments d'être constructeur non plus. Je l’aurais probablement fait accepter une fonction qui acceptait un index et renvoyait les arguments à transmettre au constructeur.

Une dernière pensée est que, parce que la seule exigence que nous plaçons sur constructor est que ce soit un callable, vous pouvez même lui transmettre tout ce qui renvoie ce que vous voulez dans votre liste. Une méthode liée qui extrait les résultats d'une requête de base de données, par exemple. C'est un peu utile trois petites lignes de code. 

5
aaronasterling

Pourquoi ne pas rester simple en ajoutant simplement la liste dans la boucle appropriée

result = []
for i in range(x):
    result.append([])
    for j in someListOfElements:
        result[i].append(j)

[Edit: Ajout d'un exemple]

>>> someListOfElements = ['a', 'b', 'c']
>>> x = 3
>>> result = []
>>> for i in range(x):
...     result.append([])
...     for j in someListOfElements:
...         result[i].append(j)
... 
>>> 
>>> result
[['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c']]
4
pyfunc

Veuillez inclure runnable sample code pour que nous puissions exécuter le code nous-mêmes et voir rapidement ce que vous voulez faire. On dirait que tu veux juste ça:

result = []
for i in range(x):
    data = []
    for j in someListOfElements:
        data.append(j)
    # or data = [j for j in someListOfElements]
    result.append(data)
0
Glenn Maynard