Anscheinend list(a)
nicht zuordnen, [x for x in a]
an einigen Stellen insgesamt [*a]
zuordnen und die ganze Zeit insgesamt zuordnen ?
Hier sind die Größen n von 0 bis 12 und die resultierenden Größen in Bytes für die drei Methoden:
0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208
So berechnet , reproduzierbar bei repl.it mit Python 3. 8 :
from sys import getsizeof
for n in range(13):
a = [None] * n
print(n, getsizeof(list(a)),
getsizeof([x for x in a]),
getsizeof([*a]))
Also: Wie funktioniert das? Wie [*a]
ordnet man insgesamt zu? Welchen Mechanismus verwendet es tatsächlich, um die Ergebnisliste aus der angegebenen Eingabe zu erstellen? Verwendet es einen Iterator a
und verwendet so etwas wie list.append
? Wo ist der Quellcode?
( Colab mit Daten und Code , die die Bilder erzeugt haben.)
Vergrößern auf kleiner n:
Verkleinern auf größer n:
python
python-3.x
list
cpython
python-internals
Stefan Pochmann
quelle
quelle
[*a]
so zu verhalten scheint, als würde manextend
eine leere Liste verwenden.list(a)
arbeitet vollständig in C; Es kann den internen Puffer Knoten für Knoten zuweisen, während er iterierta
.[x for x in a]
verwendet nurLIST_APPEND
viel, so folgt es dem normalen Muster "Gesamt ein wenig zuordnen, bei Bedarf neu zuweisen" einer normalen Liste.[*a]
verwendetBUILD_LIST_UNPACK
, die ... Ich weiß nicht, was das tut, außer anscheinend die ganze Zeit zulist(a)
und[*a]
identisch sind und beide im Vergleich zu insgesamt zugeordnet sind[x for x in a]
, sodass ...sys.getsizeof
möglicherweise nicht das richtige Werkzeug für die Verwendung ist.sys.getsizeof
ist das richtige Tool, es zeigt nur, dasslist(a)
für die Gesamtzuordnung verwendet. Eigentlich Was ist neu in Python 3.8 erwähnt es: „Die Liste Konstruktor nicht overallocate [...]“ .Antworten:
[*a]
macht intern das C-Äquivalent von :list
newlist.extend(a)
list
.Wenn Sie also Ihren Test erweitern auf:
Probieren Sie es online aus!
Sie sehen die Ergebnisse für
getsizeof([*a])
undl = []; l.extend(a); getsizeof(l)
sind gleich.Dies ist normalerweise das Richtige. Wenn
extend
Sie normalerweise erwarten, später mehr hinzuzufügen, und ähnlich wie beim allgemeinen Auspacken, wird davon ausgegangen, dass mehrere Dinge nacheinander hinzugefügt werden.[*a]
ist nicht der normale Fall; Python geht davon aus, dass demlist
([*a, b, c, *d]
) mehrere Elemente oder Iterables hinzugefügt werden , sodass die Gesamtzuordnung im allgemeinen Fall Arbeit spart.Im Gegensatz dazu kann ein
list
aus einem einzigen, vordefinierten iterierbaren (mitlist()
) konstruiertes Element während des Gebrauchs nicht wachsen oder schrumpfen, und die Gesamtzuordnung ist verfrüht, bis das Gegenteil bewiesen ist. Python hat kürzlich einen Fehler behoben, durch den der Konstruktor auch für Eingaben mit bekannter Größe insgesamt zugeordnet wurde .Das
list
Verständnis entspricht effektiv wiederholtenappend
s, sodass Sie das Endergebnis des normalen Gesamtzuordnungswachstumsmusters sehen, wenn Sie jeweils ein Element hinzufügen.Dies ist keine Sprachgarantie. Es ist nur so, wie CPython es implementiert. Die Python-Sprachspezifikation befasst sich im Allgemeinen nicht mit bestimmten Wachstumsmustern in
list
(abgesehen von der Garantie von amortisiertenO(1)
append
s undpop
s vom Ende). Wie in den Kommentaren erwähnt, ändert sich die spezifische Implementierung in 3.9 erneut. Dies wirkt sich zwar nicht auf[*a]
andere Fälle aus, in denen das, was früher "ein temporärestuple
Element erstellen und dannextend
mit demtuple
" erstellt wurde, nun zu mehreren Anwendungen von wirdLIST_APPEND
, die sich ändern können, wenn die Gesamtzuordnung erfolgt und welche Zahlen in die Berechnung einfließen.quelle
BUILD_LIST_UNPACK
, der_PyList_Extend
als C-Äquivalent zum Aufruf verwendet wirdextend
(nur direkt und nicht durch Methodensuche). Sie kombinierten es mit den Wegen zum Bauen einestuple
mit Auspacken;tuple
s lassen sich für das stückweise Erstellen nicht gut zuordnen, daher entpacken sie immer zu alist
(um von der Gesamtzuordnung zu profitieren) und konvertierentuple
am Ende zu, wenn dies angefordert wurde.BUILD_LIST
,LIST_EXTEND
für jede Sache auszupacken,LIST_APPEND
für einzelne Artikel), statt Laden alles auf dem Stapel vor dem Bau der gesamtenlist
mit einem einzigen Byte - Code - Anweisung (ermöglicht es dem Compiler auszuführen Optimierungen , dass der all-in-one Unterricht nicht erlauben, wie die Umsetzung[*a, b, *c]
alsLIST_EXTEND
,LIST_APPEND
,LIST_EXTEND
w / o wickeln , umb
in einem ein-tuple
die Anforderungen gerecht zu werdenBUILD_LIST_UNPACK
).Vollständiges Bild davon, was passiert, basierend auf den anderen Antworten und Kommentaren (insbesondere der Antwort von ShadowRanger , die auch erklärt, warum es so gemacht wird).
Demontage zeigt, dass
BUILD_LIST_UNPACK
verwendet wird:Das wird behandelt in
ceval.c
, die eine leere Liste baut und erweitert sie (mita
):_PyList_Extend
verwendetlist_extend
:Was mit der Summe der Größen ruft
list_resize
:Und das lässt sich insgesamt wie folgt zusammenfassen:
Lassen Sie uns das überprüfen. Berechnen Sie die erwartete Anzahl von Spots mit der obigen Formel und berechnen Sie die erwartete Bytegröße, indem Sie sie mit 8 multiplizieren (da ich hier 64-Bit-Python verwende) und die Bytegröße einer leeren Liste hinzufügen (dh den konstanten Overhead eines Listenobjekts). ::
Ausgabe:
Spiele mit Ausnahme
n = 0
, dielist_extend
tatsächlich Verknüpfungen , so dass tatsächlich Streichhölzer auch:quelle
Dies sind Implementierungsdetails des CPython-Interpreters und daher möglicherweise nicht konsistent für andere Interpreter.
Das heißt, Sie können sehen, wo das Verständnis und
list(a)
Verhalten hier kommen:https://github.com/python/cpython/blob/master/Objects/listobject.c#L36
Speziell für das Verständnis:
Direkt unter diesen Zeilen befindet sich
list_preallocate_exact
die, die beim Anrufen verwendet wirdlist(a)
.quelle
[*a]
Es werden nicht einzelne Elemente einzeln angehängt. Es hat einen eigenen dedizierten Bytecode, über den die Masseneinfügung erfolgtextend
.[*a]