Aujourd'hui en classe, nous avons appris que récupérer un élément d'une liste est O(1)
en Python. pourquoi est-ce le cas? Supposons que j'ai une liste de quatre éléments, par exemple:
li = ["perry", 1, 23.5, "s"]
Ces éléments ont différentes tailles en mémoire. Il n'est donc pas possible de prendre l'emplacement mémoire de li[0]
et ajoutez trois fois la taille de chaque élément pour obtenir l'emplacement mémoire de li[3]
. Alors, comment l'interprète sait où li[3]
est sans avoir à parcourir la liste pour récupérer l'élément?
Une liste en Python est implémentée comme un tableau de pointeurs1. Alors, que se passe-t-il vraiment lorsque vous créez la liste:
["perry", 1, 23.5, "s"]
est que vous créez en fait un tableau de pointeurs comme ceci:
[0xa3d25342, 0x635423fa, 0xff243546, 0x2545fade]
Chaque pointeur "pointe" vers les objets respectifs en mémoire, de sorte que la chaîne "perry"
sera stocké à l'adresse 0xa3d25342
et le nombre 1
sera stocké dans 0x635423fa
, etc.
Comme tous les pointeurs ont la même taille, l'interpréteur peut en fait ajouter 3 fois la taille d'un élément à l'adresse de li[0]
pour accéder au pointeur stocké dans li[3]
.
1 Obtenez plus de détails sur: la bouche du cheval (code source CPython sur GitHub) .
Quand tu dis a = [...]
, a
est en fait un pointeur vers un PyObject
contenant un tableau de pointeurs vers PyObject
s.
Lorsque vous demandez a[2]
, l'interpréteur suit d'abord le pointeur sur le PyObject
de la liste, puis ajoute 2
à l'adresse du tableau qu'il contient, puis renvoie ce pointeur. La même chose se produit si vous demandez a[0]
ou a[9999]
.
Fondamentalement, tous les objets Python sont accessibles par référence plutôt que par valeur, même les littéraux entiers comme 2
. Il y a juste quelques astuces dans le système de pointeurs pour garder tout cela efficace. Et les pointeurs ont une taille connue, ils peuvent donc être stockés de manière pratique dans des tableaux de style C.
Réponse courte: Python sont des tableaux.
Réponse longue: Le terme informatique liste signifie généralement soit une liste à liaison unique (telle qu'utilisée en programmation fonctionnelle) soit une liste à double liaison (telle qu'utilisée en programmation procédurale). Ces structures de données prennent en charge O(1) insertion en tête de liste (fonctionnellement) ou à toute position qui n'a pas besoin d'être recherchée (procéduralement). A Python `` liste '' n'a aucune de ces caractéristiques. Au lieu de cela, il prend en charge (amorti) O(1) en ajoutant à la fin de la liste (comme un vecteur C++ std :: vector ou Java ArrayList). Python sont vraiment des tableaux redimensionnables en termes CS.
Le commentaire suivant extrait de la documentation Python explique certaines des caractéristiques de performance de Python `` listes ''):
Il est également possible d'utiliser une liste comme file d'attente, où le premier élément ajouté est le premier élément récupéré ("premier entré, premier sorti"); cependant, les listes ne sont pas efficaces à cette fin. Alors que les ajouts et les sauts à la fin de la liste sont rapides, les insertions ou les sauts depuis le début d'une liste sont lents (car tous les autres éléments ont être décalé d'un).