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.
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.
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 .
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.
Ceci dépend de la mise en œuvre, mais le IIRC:
ArrayList
Ainsi, ils ont tous O(1) accès aléatoire.
Selon le documentation ,
Les listes de Python sont vraiment des tableaux de longueur variable, pas des listes liées de style LISP.
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?
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.
...
Insérer
Insérons un nouvel entier (5) en position 1: l.insert (1,5) et voyons ce qui se passe en interne.
...
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 .
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 .
...
Remove Python L'objet de liste a une méthode pour supprimer un élément spécifique: l.remove (5) .
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).
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.