Warum ist []
schneller als list()
?
Der Hauptgrund dafür ist, dass Python list()
genau wie eine benutzerdefinierte Funktion behandelt wird. Dies bedeutet, dass Sie sie abfangen können, indem Sie etwas anderes aliasen und etwas anderes list
tun (z. B. Ihre eigene Unterklassenliste oder möglicherweise eine Deque verwenden).
Es wird sofort eine neue Instanz einer eingebauten Liste mit erstellt []
.
Meine Erklärung versucht, Ihnen die Intuition dafür zu geben.
Erläuterung
[]
ist allgemein als wörtliche Syntax bekannt.
In der Grammatik wird dies als "Listenanzeige" bezeichnet. Aus den Dokumenten :
Eine Listenanzeige ist eine möglicherweise leere Reihe von Ausdrücken in eckigen Klammern:
list_display ::= "[" [starred_list | comprehension] "]"
Eine Listenanzeige ergibt ein neues Listenobjekt, wobei der Inhalt entweder durch eine Liste von Ausdrücken oder durch ein Verständnis angegeben wird. Wenn eine durch Kommas getrennte Liste von Ausdrücken angegeben wird, werden ihre Elemente von links nach rechts ausgewertet und in dieser Reihenfolge in das Listenobjekt eingefügt. Wenn ein Verständnis geliefert wird, wird die Liste aus den Elementen erstellt, die sich aus dem Verständnis ergeben.
Kurz gesagt bedeutet dies, dass ein eingebautes Objekt vom Typ ist list
erstellt wird.
Dies lässt sich nicht umgehen - was bedeutet, dass Python dies so schnell wie möglich tun kann.
Auf der anderen Seite list()
kann das Erstellen eines integrierten Systems list
mithilfe des integrierten Listenkonstruktors abgefangen werden .
Angenommen, wir möchten, dass unsere Listen geräuschvoll erstellt werden:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Wir könnten dann den Namen list
auf globaler Ebene auf Modulebene abfangen , und wenn wir dann eine erstellen list
, erstellen wir tatsächlich unsere Liste mit Untertypen:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
Ebenso könnten wir es aus dem globalen Namespace entfernen
del list
und setzen Sie es in den eingebauten Namespace:
import builtins
builtins.list = List
Und nun:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
Beachten Sie, dass die Listenanzeige bedingungslos eine Liste erstellt:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Wir machen das wahrscheinlich nur vorübergehend, also lassen Sie uns unsere Änderungen rückgängig machen - entfernen Sie zuerst das neue List
Objekt aus den eingebauten:
>>> del builtins.list
>>> builtins.list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'builtins' has no attribute 'list'
>>> list()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'list' is not defined
Oh nein, wir haben den Überblick über das Original verloren.
Keine Sorge, wir können immer noch bekommen list
- es ist die Art eines Listenliteral:
>>> builtins.list = type([])
>>> list()
[]
Damit...
Warum ist []
schneller als list()
?
Wie wir gesehen haben - wir können überschreiben list
-, aber wir können die Erstellung des Literaltyps nicht abfangen. Wenn wir verwenden, müssen list
wir die Suche durchführen, um zu sehen, ob etwas da ist.
Dann müssen wir anrufen, was auch immer wir nachgeschlagen haben. Aus der Grammatik:
Ein Aufruf ruft ein aufrufbares Objekt (z. B. eine Funktion) mit einer möglicherweise leeren Reihe von Argumenten auf:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Wir können sehen, dass es für jeden Namen dasselbe tut, nicht nur für die Liste:
>>> import dis
>>> dis.dis('list()')
1 0 LOAD_NAME 0 (list)
2 CALL_FUNCTION 0
4 RETURN_VALUE
>>> dis.dis('doesnotexist()')
1 0 LOAD_NAME 0 (doesnotexist)
2 CALL_FUNCTION 0
4 RETURN_VALUE
Denn []
auf Python-Bytecode-Ebene gibt es keinen Funktionsaufruf:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Es geht einfach direkt zum Erstellen der Liste ohne Nachschlagen oder Aufrufe auf Bytecode-Ebene.
Fazit
Wir haben gezeigt, dass list
mit Benutzercode unter Verwendung der Scoping-Regeln abgefangen werden kann, und daslist()
nach einem aufrufbaren gesucht und dieser dann aufgerufen wird.
Während []
ist eine Listenanzeige oder ein Literal, und vermeidet so die Namenssuche und Funktionsaufruf.
()
und''
sind etwas Besonderes, da sie nicht nur leer, sondern auch unveränderlich sind. Daher ist es ein einfacher Gewinn, sie zu Singletons zu machen. Sie konstruieren nicht einmal neue Objekte, sondern laden nur den Singleton für das leeretuple
/str
. Technisch gesehen ein Implementierungsdetail, aber ich kann mir nur schwer vorstellen, warum sie das leere / aus Leistungsgründen nicht zwischenspeichern würden . Ihre Intuition über und die Rückgabe eines Aktienliteral war also falsch, aber es gilt für und .tuple
str
[]
{}
()
''