Antworten:
Es ist ein dynamisches Array . Praktischer Beweis: Die Indizierung dauert (natürlich mit extrem kleinen Unterschieden (0,0013 µs!)) Unabhängig vom Index dieselbe Zeit:
...>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
Ich wäre erstaunt, wenn IronPython oder Jython verknüpfte Listen verwenden würden - sie würden die Leistung vieler weit verbreiteter Bibliotheken ruinieren, die auf der Annahme beruhen, dass Listen dynamische Arrays sind.
x=[None]*1000
, so dass die Messung möglicher Listenzugriffsunterschiede eher ungenau bleibt . Sie müssen die Initialisierung trennen:-s "x=[None]*100" "x[0]"
Der C-Code ist eigentlich ziemlich einfach. Wenn Sie ein Makro erweitern und einige irrelevante Kommentare entfernen, befindet sich die Grundstruktur in listobject.h
, die eine Liste definiert als:
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
enthält einen Referenzzähler und eine Typkennung. Es ist also ein Vektor / Array, der insgesamt zugeordnet wird. Der Code zum Ändern der Größe eines solchen Arrays, wenn es voll ist, ist in listobject.c
. Das Array wird nicht verdoppelt, sondern durch Zuweisung vergrößert
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
auf die Kapazität jedes Mal, wo newsize
ist die angeforderte Größe (nicht unbedingt, allocated + 1
weil Sie extend
durch eine beliebige Anzahl von Elementen können, anstatt append
sie einzeln zu geben).
Siehe auch die Python-FAQ .
array
Modul oder NumPy vorzuziehen.
Dies ist implementierungsabhängig, aber IIRC:
ArrayList
Somit haben alle O (1) Direktzugriff.
O(1)
Indizierung von Listen jedoch eine weit verbreitete und gültige Annahme ist, würde keine Implementierung es wagen, sie zu brechen.
Ich würde Laurent Luces Artikel "Python List Implementation" vorschlagen . War für mich sehr nützlich, da der Autor erklärt, wie die Liste in CPython implementiert ist, und für diesen Zweck hervorragende Diagramme verwendet.
Listen Sie die Struktur des Objekts C auf
Ein Listenobjekt in CPython wird durch die folgende C-Struktur dargestellt.
ob_item
ist eine Liste von Zeigern auf die Listenelemente. zugewiesen ist die Anzahl der im Speicher zugewiesenen Steckplätze.typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
Es ist wichtig, den Unterschied zwischen zugewiesenen Slots und der Größe der Liste zu beachten. Die Größe einer Liste entspricht der
len(l)
. Die Anzahl der zugewiesenen Steckplätze ist die im Speicher zugewiesene. Oft werden Sie feststellen, dass die zugewiesene Größe größer als die Größe sein kann. Auf diese Weise müssen Sie nichtrealloc
jedes Mal aufrufen, wenn ein neues Element an die Liste angehängt wird.
...
Anhängen
Wir fügen der Liste eine Ganzzahl hinzu :
l.append(1)
. Was geschieht?
Wir fahren fort, indem wir ein weiteres Element hinzufügen :
l.append(2)
.list_resize
wird mit n + 1 = 2 aufgerufen, aber da die zugewiesene Größe 4 ist, muss kein weiterer Speicher zugewiesen werden. Das gleiche passiert, wenn wir 2 weitere Ganzzahlen hinzufügen:l.append(3)
,l.append(4)
. Das folgende Diagramm zeigt, was wir bisher haben.
...
Einfügen
Fügen wir an Position 1 eine neue Ganzzahl (5) ein:
l.insert(1,5)
und schauen wir uns an, was intern passiert.
...
Pop
Wenn Sie das letzte Element einfügen
l.pop()
,listpop()
wird: aufgerufen.list_resize
wird im Inneren aufgerufenlistpop()
und wenn die neue Größe weniger als die Hälfte der zugewiesenen Größe beträgt, wird die Liste verkleinert.Sie können beobachten, dass Slot 4 immer noch auf die Ganzzahl zeigt, aber das Wichtigste ist die Größe der Liste, die jetzt 4 ist. Lassen Sie uns ein weiteres Element hinzufügen. In
list_resize()
ist Größe - 1 = 4 - 1 = 3 weniger als die Hälfte der zugewiesenen Slots, sodass die Liste auf 6 Slots verkleinert wird und die neue Größe der Liste jetzt 3 ist.Sie können beobachten, dass Slot 3 und 4 immer noch auf einige Ganzzahlen verweisen, aber das Wichtigste ist die Größe der Liste, die jetzt 3 ist.
...
Python- Listenobjekt entfernen verfügt über eine Methode zum Entfernen eines bestimmten Elements :
l.remove(5)
.
aggregation
, nicht composition
. Ich wünschte, es gäbe auch eine Liste mit Kompositionen.
Laut Dokumentation ,
Pythons Listen sind wirklich Arrays variabler Länge, keine verknüpften Listen im Lisp-Stil.
Wie andere oben angegeben haben, werden die Listen (wenn sie beträchtlich groß sind) implementiert, indem eine feste Menge an Speicherplatz zugewiesen wird und, falls dieser Speicherplatz gefüllt werden sollte, eine größere Menge an Speicherplatz zugewiesen und über die Elemente kopiert wird.
Um zu verstehen, warum die Methode ohne Verlust der Allgemeinheit O (1) amortisiert wird, nehmen wir an, dass wir a = 2 ^ n Elemente eingefügt haben und nun unsere Tabelle auf 2 ^ (n + 1) Größe verdoppeln müssen. Das heißt, wir führen derzeit 2 ^ (n + 1) Operationen aus. Letzte Kopie, wir haben 2 ^ n Operationen durchgeführt. Vorher haben wir 2 ^ (n-1) gemacht ... bis hinunter zu 8,4,2,1. Wenn wir diese addieren, erhalten wir 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^ n) = O (a) Gesamtinsertionen (dh O (1) Amortisationszeit). Es sollte auch beachtet werden, dass, wenn die Tabelle Löschungen zulässt, das Schrumpfen der Tabelle mit einem anderen Faktor erfolgen muss (z. B. 3x).
Eine Liste in Python ist so etwas wie ein Array, in dem Sie mehrere Werte speichern können. Die Liste ist veränderbar, dh Sie können sie ändern. Das Wichtigste, was Sie wissen sollten, wenn wir eine Liste erstellen, erstellt Python automatisch eine Referenz-ID für diese Listenvariable. Wenn Sie es ändern, indem Sie eine andere Variable zuweisen, wird die Hauptliste geändert. Versuchen wir es mit einem Beispiel:
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']
Wir fügen hinzu, my_list
aber unsere Hauptliste hat sich geändert. Die Liste dieses Mittelwerts wurde nicht als Kopierliste zugewiesen, die als Referenz zugewiesen wurde.