Pourquoi est []
plus rapide que list()
?
La principale raison est que Python est traité list()
comme une fonction définie par l'utilisateur, ce qui signifie que vous pouvez l'intercepter en aliasant quelque chose d'autre list
et en faisant quelque chose de différent (comme utiliser votre propre liste de sous-classes ou peut-être un deque).
Il crée immédiatement une nouvelle instance d'une liste intégrée avec []
.
Mon explication cherche à vous en donner l'intuition.
Explication
[]
est communément appelée syntaxe littérale.
Dans la grammaire, cela est appelé un "affichage de liste". De la documentation :
Un affichage de liste est une série d'expressions éventuellement vide entre crochets:
list_display ::= "[" [starred_list | comprehension] "]"
Un affichage de liste produit un nouvel objet de liste, le contenu étant spécifié par une liste d'expressions ou une compréhension. Lorsqu'une liste d'expressions séparées par des virgules est fournie, ses éléments sont évalués de gauche à droite et placés dans l'objet liste dans cet ordre. Lorsqu'une compréhension est fournie, la liste est construite à partir des éléments résultant de la compréhension.
En bref, cela signifie qu'un objet intégré de type list
est créé.
Il n'y a aucun moyen de contourner cela - ce qui signifie que Python peut le faire aussi rapidement qu'il le peut.
D'un autre côté, il list()
peut être intercepté de créer une fonction intégrée list
à l'aide du constructeur de liste intégrée.
Par exemple, disons que nous voulons que nos listes soient créées bruyamment:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Nous pourrions alors intercepter le nom list
sur la portée globale au niveau du module, puis lorsque nous créons un list
, nous créons en fait notre liste de sous-types:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
De même, nous pourrions le supprimer de l'espace de noms global
del list
et le mettre dans l'espace de noms intégré:
import builtins
builtins.list = List
Et maintenant:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
Et notez que l'affichage de la liste crée une liste sans condition:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Nous ne le faisons probablement que temporairement, alors annulons nos modifications - List
supprimons d' abord le nouvel objet des commandes internes:
>>> 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, non, nous avons perdu la trace de l'original.
Ne vous inquiétez pas, nous pouvons toujours obtenir list
- c'est le type d'une liste littérale:
>>> builtins.list = type([])
>>> list()
[]
Donc...
Pourquoi est []
plus rapide que list()
?
Comme nous l'avons vu - nous pouvons écraser list
- mais nous ne pouvons pas intercepter la création du type littéral. Lorsque nous utilisons, list
nous devons faire des recherches pour voir s'il y a quelque chose.
Ensuite, nous devons appeler tout appelable que nous avons recherché. De la grammaire:
Un appel appelle un objet appelable (par exemple, une fonction) avec une série d'arguments éventuellement vide:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Nous pouvons voir qu'il fait la même chose pour n'importe quel nom, pas seulement pour la 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
Car []
il n'y a pas d'appel de fonction au niveau du bytecode Python:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Cela va simplement à la construction de la liste sans recherche ni appel au niveau du bytecode.
Conclusion
Nous avons démontré qu'il list
peut être intercepté avec du code utilisateur à l'aide des règles de portée, et qu'il list()
recherche un appelable, puis l'appelle.
Considérant que []
est un affichage de liste, ou un littéral, et évite ainsi la recherche de nom et l'appel de fonction.
()
et''
sont spéciaux, car ils ne sont pas seulement vides, ils sont immuables, et en tant que tels, il est facile de les rendre singletons; ils ne construisent même pas de nouveaux objets, il suffit de charger le singleton pour letuple
/ videstr
. Techniquement, un détail d'implémentation, mais j'ai du mal à imaginer pourquoi ils ne mettraient pas en cache le videtuple
/str
pour des raisons de performances. Donc, votre intuition à propos[]
et le{}
transfert d'un littéral boursier était faux, mais cela s'applique à()
et''
.