web-dev-qa-db-fra.com

Initialisation d'une liste avec un nombre connu d'éléments dans Python

En ce moment, j'utilise une liste et je m'attendais à quelque chose comme:

verts = list (1000)

Devrais-je utiliser un tableau à la place?

225
Joan Venge

La première chose qui me vient à l’esprit est la suivante:

verts = [None]*1000

Mais avez-vous vraiment besoin de le préinitialiser?

329
Steve Losh

Vous ne savez pas trop pourquoi tout le monde vous donne du fil à retordre, il existe plusieurs scénarios dans lesquels vous souhaiteriez une liste initialisée de taille fixe. Et vous avez correctement déduit que les tableaux sont sensibles dans ces cas.

import array
verts=array.array('i',(0,)*1000)

Pour les non-pythonistes, le terme (0,)*1000 crée un tuple contenant 1 000 zéros. La virgule oblige python à reconnaître (0) comme un tuple, sinon il serait évalué à 0.

J'ai utilisé un tuple au lieu d'une liste car ils ont généralement des frais généraux moins élevés.

70
FoxyLad

Un moyen évident et probablement pas efficace est

verts = [0 for x in range(1000)]

Notez que cela peut être étendu à 2 dimensions facilement. Par exemple, pour obtenir un "tableau" 10x100, vous pouvez faire

verts = [[0 for x in range(100)] for y in range(10)]
60
e-shy

Vouloir intégrer une matrice de taille fixe est une chose parfaitement acceptable dans n'importe quel langage de programmation; ce n'est pas comme si le programmeur voulait mettre une instruction break dans une boucle while (true). Croyez-moi, en particulier si les éléments vont juste être écrasés et pas simplement ajoutés/soustraits, comme c'est le cas de nombreux algorithmes de programmation dynamique, vous ne voulez pas déranger les instructions append et vérifier si l'élément n'a pas été encore initialisé à la volée (ça fait beaucoup de gents).

object = [0 for x in range(1000)]

Cela fonctionnera pour ce que le programmeur essaie de réaliser.

30
Miko

@Steve a déjà donné une bonne réponse à votre question:

verts = [None] * 1000

Attention: Comme l'a souligné @ Joachim Wuttke, la liste doit être initialisée avec un élément immuable. [[]] * 1000 ne fonctionne pas comme prévu, car vous obtiendrez une liste de 1000 listes identiques (similaire à une liste de 1000 points de la même liste en C). Les objets immuables comme int, str ou Tuple iront bien.

Des alternatives

Le redimensionnement des listes est lent. Les résultats suivants ne sont pas très surprenants:

>>> N = 10**6

>>> %timeit a = [None] * N
100 loops, best of 3: 7.41 ms per loop

>>> %timeit a = [None for x in xrange(N)]
10 loops, best of 3: 30 ms per loop

>>> %timeit a = [None for x in range(N)]
10 loops, best of 3: 67.7 ms per loop

>>> a = []
>>> %timeit for x in xrange(N): a.append(None)
10 loops, best of 3: 85.6 ms per loop

Mais le redimensionnement n’est pas très lent si vous n’avez pas de très grandes listes. Au lieu d'initialiser la liste avec un seul élément (par exemple, None) et une longueur fixe pour éviter le redimensionnement de la liste, vous devez envisager d'utiliser la compréhension de liste et de remplir directement la liste avec les valeurs correctes. Par exemple:

>>> %timeit a = [x**2 for x in xrange(N)]
10 loops, best of 3: 109 ms per loop

>>> def fill_list1():
    """Not too bad, but complicated code"""
    a = [None] * N
    for x in xrange(N):
        a[x] = x**2
>>> %timeit fill_list1()
10 loops, best of 3: 126 ms per loop

>>> def fill_list2():
    """This is slow, use only for small lists"""
    a = []
    for x in xrange(N):
        a.append(x**2)
>>> %timeit fill_list2()
10 loops, best of 3: 177 ms per loop

Comparaison avec numpy

Pour des ensembles de données volumineux, numpy ou d'autres bibliothèques optimisées sont beaucoup plus rapides:

from numpy import ndarray, zeros
%timeit empty((N,))
1000000 loops, best of 3: 788 ns per loop

%timeit zeros((N,))
100 loops, best of 3: 3.56 ms per loop
17
lumbric

Vous pourriez faire ceci:

verts = list(xrange(1000))

Cela vous donnerait une liste de 1000 éléments en taille et qui se trouve être initialisée avec des valeurs de 0 à 999. Comme list fait un __len__ premier pour dimensionner la nouvelle liste, il devrait être assez efficace.

4
John Montgomery

Cette:

 lst = [8 for i in range(9)]

crée une liste, les éléments sont initialisés 8

mais ça:

lst = [0] * 7

créerait 7 listes qui ont un élément

0
Jutta

Vous devriez envisager d'utiliser un type dict au lieu d'une liste pré-initialisée. Le coût d'une recherche dans un dictionnaire est faible et comparable au coût d'accès à un élément de liste arbitraire.

Et lorsque vous utilisez un mappage, vous pouvez écrire:

aDict = {}
aDict[100] = fetchElement()
putElement(fetchElement(), fetchPosition(), aDict)

Et la fonction putElement peut stocker un élément à n’importe quelle position. Et si vous avez besoin de vérifier si votre collection contient un élément à un index donné, il est préférable d’écrire en Pythonic:

if anIndex in aDict:
    print "cool!"

Que:

if not myList[anIndex] is None:
    print "cool!"

Puisque ce dernier suppose qu'aucun élément réel de votre collection ne peut être None. Et si cela se produit, votre code se comporte mal.

Et si vous avez désespérément besoin de performances et que vous tentez de pré-initialiser vos variables et écrivez le code le plus rapide possible, changez de langue. Le code le plus rapide ne peut pas être écrit en Python. Vous devriez plutôt essayer C et implémenter des wrappers pour appeler votre code pré-initialisé et pré-compilé à partir de Python.

0
Abgan