291 votes

Comment la liste de Python est-elle mise en œuvre ?

Est-ce une liste liée, un tableau ? J'ai cherché partout et je n'ai trouvé que des gens qui devinaient. Mes connaissances en C ne sont pas assez bonnes pour regarder le code source.

0 votes

Selon les docs Les listes Python ne sont pas des listes liées. Ce sont des tableaux de taille variable. Elles sont également mutables. Je ne suis pas sûr qu'il implémente vraiment une capacité logique et une capacité réelle (ce qui en ferait une liste complète). réseau dynamique . On peut donc dire que c'est une structure de données unique en son genre. (Même si je crois vraiment que c'est un tableau dynamique).

311voto

larsmans Points 167484

Le code C est assez simple, en fait. En augmentant une macro et en supprimant quelques commentaires non pertinents, la structure de base est la suivante listobject.h qui définit une liste comme suit :

typedef struct {
    PyObject_HEAD
    Py_ssize_t ob_size;

    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     */
    Py_ssize_t allocated;
} PyListObject;

PyObject_HEAD contient un nombre de références et un identifiant de type. Donc, c'est un vecteur/rayon qui sur-alloue. Le code pour redimensionner un tel tableau quand il est plein est dans listobject.c . Il ne double pas réellement le tableau, mais le fait croître en allouant

new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;

à la capacité à chaque fois, où newsize est la taille demandée (pas nécessairement allocated + 1 parce que vous pouvez extend par un nombre arbitraire d'éléments au lieu de append en les prenant un par un).

Voir aussi le FAQ Python .

13 votes

Ainsi, lorsque l'on itère sur des listes python, c'est aussi lent que des listes chaînées, parce que chaque entrée n'est qu'un pointeur, donc chaque élément est susceptible d'entraîner une absence de cache.

17 votes

@Kr0e : pas si les éléments suivants sont en fait le même objet :) Mais si vous avez besoin de structures de données plus petites et plus conviviales pour le cache, la fonction array ou NumPy sont à privilégier.

6 votes

@Kr0e Je ne dirais pas que l'itération sur la liste est aussi lente que les listes chaînées, mais que l'itération sur les listes chaînées est aussi lente que les listes chaînées. valeurs des listes liées est aussi lente qu'une liste liée, avec la réserve que Fred a mentionnée. Par exemple, l'itération sur une liste pour la copier dans une autre devrait être plus rapide qu'une liste chaînée.

82voto

delnan Points 52260

C'est un réseau dynamique . Preuve pratique : L'indexation prend (bien sûr avec des différences extrêmement faibles (0,0013 µsecs !)) le même temps quel que soit l'index :

...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop

...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop

Je serais stupéfait si IronPython ou Jython utilisaient des listes liées - ils ruineraient les performances de nombreuses bibliothèques largement utilisées, construites sur l'hypothèse que les listes sont des tableaux dynamiques.

1 votes

Belle preuve de concept. Mes résultats pour x[0] y x[500] n'étaient pas exactement égaux, mais ils étaient très proches (assez proches pour être une erreur d'arrondi ou juste des différences dans l'utilisation du CPU). Au fait, mon CPU est meilleur que le vôtre :)

4 votes

@Ralf : Je sais que mon processeur (ainsi que la plupart des autres matériels, d'ailleurs) est vieux et lent - d'un autre côté, je peux supposer que le code qui tourne assez vite pour moi est assez rapide pour tous les utilisateurs :D

1 votes

@delnan : Python fonctionne presque toujours assez vite pour tous les utilisateurs.

49voto

NullUserException Points 42268

Cela dépend de l'implémentation, mais IIRC :

  • CPython utilise un tableau de pointeurs
  • Jython utilise un ArrayList
  • IronPython utilise apparemment aussi un tableau. Vous pouvez parcourir le code source pour le découvrir.

Ils ont donc tous un accès aléatoire O(1).

2 votes

Dépendant de l'implémentation, c'est-à-dire qu'un interprète python qui implémenterait les listes comme des listes liées serait une implémentation valide du langage python ? En d'autres termes : L'accès aléatoire O(1) aux listes n'est pas garanti ? Cela ne rend-il pas impossible l'écriture d'un code efficace sans dépendre des détails de l'implémentation ?

3 votes

@sepp Je crois que les listes en Python sont juste des collections ordonnées ; l'implémentation et/ou les exigences de performance de cette implémentation ne sont pas explicitement indiquées.

7 votes

@sppe2k : Puisque Python n'a pas vraiment de standard ou de spécification formelle (bien qu'il y ait quelques morceaux de documentations qui disent " ... est garanti pour ... "), vous ne pouvez pas être sûr à 100% comme dans " ceci est garanti par un morceau de papier ". Mais comme O(1) pour l'indexation des listes est une hypothèse assez commune et valide, aucune implémentation n'oserait la briser.

41voto

Amber Points 159296

En CPython, les listes sont des tableaux de pointeurs. D'autres implémentations de Python peuvent choisir de les stocker de manière différente.

27voto

ravi77o Points 101

Selon le documentation ,

Les listes de Python sont en fait des tableaux de longueur variable, et non des listes liées de type Lisp.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X