web-dev-qa-db-fra.com

Comment la liste de Python est-elle implémentée?

Est-ce une liste chaînée, un tableau? J'ai cherché autour de moi et n'ai trouvé que des gens qui devinaient. Ma connaissance du C n'est pas suffisante pour examiner le code source.

149
Greg

C'est un tableau dynamique . Preuve pratique: l'indexation prend (bien sûr avec des différences extrêmement petites (0,0013 µsec!)) 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 étonné si IronPython ou Jython utilisaient des listes chaînées - elles gâcheraient les performances de nombreuses bibliothèques largement utilisées, basées sur l'hypothèse que les listes sont des tableaux dynamiques.

44
user395760

Le code C est assez simple, en fait. En développant une macro et en supprimant des commentaires non pertinents, la structure de base est en 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 identificateur de type. Donc, c'est un vecteur/tableau qui se généralise. Le code permettant de redimensionner un tel tableau lorsqu'il est plein est en listobject.c . Il ne double pas réellement le tableau, mais se développe 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 'un par un).

Voir aussi le FAQ Python .

200
Fred Foo

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

29
Amber

Ceci dépend de la mise en œuvre, mais le 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 savoir.

Ainsi, ils ont tous O(1) accès aléatoire.

27
NullUserException

Selon le documentation ,

Les listes de Python sont vraiment des tableaux de longueur variable, pas des listes liées de style LISP.

22
ravi77o

Je suggérerais article de Laurent Luce "Implémentation de liste Python" . Cela m'a été très utile car l'auteur explique comment la liste est implémentée dans CPython et utilise d'excellents diagrammes à cette fin.

Liste la structure de l'objet C

Un objet de liste dans CPython est représenté par la structure C suivante. ob_item est une liste de pointeurs sur les éléments de la liste. alloué est le nombre d'emplacements alloués en mémoire.

typedef struct {

PyObject_VAR_HEAD

PyObject ** ob_item;

Py_ssize_t alloué;

} PyListObject;

Il est important de noter la différence entre les créneaux attribués et la taille de la liste. La taille d'une liste est la même que len (l). Le nombre d'emplacements alloués est ce qui a été alloué en mémoire. Vous constaterez souvent que les ressources allouées peuvent être supérieures à la taille. Cela évite d'avoir à appeler realloc chaque fois qu'un nouvel élément est ajouté à la liste.

...

Ajouter

Nous ajoutons un entier à la liste: l.append (1). Ce qui se produit?
enter image description here

Nous continuons en ajoutant un élément supplémentaire: l.append (2). list_resize est appelé avec n + 1 = 2 mais étant donné que la taille allouée est 4, il n'est pas nécessaire d'allouer plus de mémoire. La même chose se produit lorsque nous ajoutons 2 entiers supplémentaires: l.append (3), l.append (4). Le diagramme suivant montre ce que nous avons jusqu'à présent.

enter image description here

...

Insérer

Insérons un nouvel entier (5) en position 1: l.insert (1,5) et voyons ce qui se passe en interne. enter image description here

...

Pop

Lorsque vous ouvrez le dernier élément: l.pop (), listpop () est appelé. list_resize est appelé à l'intérieur de listpop () et si la nouvelle taille est inférieure à la moitié de la taille allouée, la liste est réduite . enter image description here

Vous pouvez observer que l’emplacement 4 pointe toujours sur le nombre entier, mais l’important est la taille de la liste qui est maintenant 4. Laissons apparaître un élément supplémentaire. Dans list_resize (), size - 1 = 4 - 1 = 3 correspond à moins de la moitié des emplacements alloués. La liste est donc réduite à 6 emplacements et la nouvelle taille de la liste est désormais 3.

Vous pouvez observer que les emplacements 3 et 4 pointent toujours sur des entiers mais l’important est la taille de la liste qui est maintenant 3 . enter image description here

...

Remove Python L'objet de liste a une méthode pour supprimer un élément spécifique: l.remove (5) . enter image description here

20
Lesya

Comme d'autres l'ont dit plus haut, les listes (lorsqu'elles sont grandes) sont mises en œuvre en allouant un espace fixe et, si cet espace doit être rempli, en allouant un espace plus important et en copiant les éléments.

Pour comprendre pourquoi la méthode est O(1) amortie, sans perte de généralité, supposons que nous ayons inséré a = 2 ^ n éléments et que nous devons maintenant doubler notre tableau à 2 ^ (n + 1). Taille. Cela signifie que nous effectuons actuellement 2 ^ (n + 1) opérations. Dernière copie, nous avons effectué 2 ^ n opérations. Avant cela, nous faisions 2 ^ (n-1) ... jusqu’à 8,4,2,1. Si nous additionnons ces valeurs, nous obtenons 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^ n) = O(a) nombre total d'insertions (c'est-à-dire O(1) temps amorti). En outre, il convient de noter que si la table permet des suppressions, la réduction de la table doit être effectuée avec un facteur différent (par exemple 3x).

5
RussellStewart

Une liste dans Python est un peu comme un tableau, où vous pouvez stocker plusieurs valeurs. List est modifiable, ce qui signifie que vous pouvez le changer. La chose la plus importante à savoir, lors de la création d'une liste, Python crée automatiquement un reference_id pour cette variable de liste. Si vous la modifiez en affectant une autre variable, la liste principale sera modifiée. Essayons avec un exemple:

list_one = [1,2,3,4]

my_list = list_one

#my_list: [1,2,3,4]

my_list.append("new")

#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']

Nous annexons my_list mais notre liste principale a changé. La liste de ce moyen n'a pas été assignée comme une liste de copie assignée comme référence.

1
hasib