web-dev-qa-db-fra.com

Pourquoi '() is ()' retourne True quand '[] est []' et '{} est {}' retourne False?

D'après ce que je savais, utiliser [], {} Ou () Pour instancier des objets renvoie une nouvelle instance de list, dict Ou Tuple respectivement; un nouvel objet d'instance avec une nouvelle identité.

C'était assez clair pour moi jusqu'à ce que je le teste et j'ai remarqué que () is () Renvoie en fait True au lieu du False attendu:

>>> () is (), [] is [], {} is {}
(True, False, False)

comme prévu, ce comportement se manifeste également lors de la création d'objets avec list() , dict() et Tuple() respectivement:

>>> Tuple() is Tuple(), list() is list(), dict() is dict()
(True, False, False)

La seule information pertinente que j'ai pu trouver dans les documents pour Tuple() indique:

[...] Par exemple, Tuple('abc') renvoie ('a', 'b', 'c') Et Tuple([1, 2, 3]) renvoie (1, 2, 3). Si aucun argument n'est donné, le constructeur crée un nouveau Tuple vide, ().

Il suffit de dire que ce n'est pas suffisant pour répondre à ma question.

Alors, pourquoi les tuples vides ont-ils la même identité alors que d'autres comme les listes ou les dictionnaires n'en ont pas?

En bref:

Python crée en interne une liste C d'objets Tuple dont le premier élément contient le Tuple vide. Chaque fois que Tuple() ou () Est utilisé, Python renverra l'objet existant contenu dans la liste C susmentionnée et ne créera pas de nouveau une.

Un tel mécanisme n'existe pas pour les objets dict ou list qui sont au contraire recréés à partir de zéro à chaque fois.

Cela est probablement lié au fait que les objets immuables (comme les tuples) ne peuvent pas être modifiés et, en tant que tels, sont garantis de ne pas changer pendant l'exécution. Ceci est encore renforcé lorsque l'on considère que frozenset() is frozenset() renvoie True; comme () un frozensetvide est considéré comme un singleton dans l'implémentation de CPython . Avec les objets mutables, de telles garanties ne sont pas en place et, en tant que tel, il n'y a aucune incitation à mettre en cache leurs instances d'élément zéro (c'est-à-dire que leur contenu pourrait changer avec l'identité restant la même).

Remarque: Ce n'est pas quelque chose dont on devrait dépendre, c'est-à-dire qu'il ne faut pas considérer les tuples vides comme des singletons. Aucune garantie de ce type n'est explicitement faite dans la documentation, il faut donc supposer que cela dépend de l'implémentation.


Comment cela se fait:

Dans le cas le plus courant, l'implémentation de CPython est compilée avec deux macros PyTuple_MAXFREELIST et PyTuple_MAXSAVESIZE définie sur positive entiers. La valeur positive de ces macros entraîne la création d'un tableau d'objets Tuple de taille PyTuple_MAXSAVESIZE.

Lorsque PyTuple_New Est appelé avec le paramètre size == 0, Il s'assure de ajouter un nouveau Tuple vide à la liste s'il n'existe pas déjà:

if (size == 0) {
    free_list[0] = op;
    ++numfree[0];
    Py_INCREF(op);          /* extra INCREF so that this is never freed */
}

Ensuite, si un nouveau Tuple vide est demandé, celui qui se trouve dans la première position de cette liste va être renvoyé au lieu d'une nouvelle instance:

if (size == 0 && free_list[0]) {
    op = free_list[0];
    Py_INCREF(op);
    /* rest snipped for brevity.. */

Une raison supplémentaire qui incite à le faire est le fait que les appels de fonction construisent un Tuple pour contenir les arguments positionnels qui vont être utilisés. Cela peut être vu dans la fonction load_args dans ceval.c:

static PyObject *
load_args(PyObject ***pp_stack, int na)
{
    PyObject *args = PyTuple_New(na);
    /* rest snipped for brevity.. */

qui est appelé via do_call dans le même fichier. Si le nombre d'arguments na est nul, un Tuple vide va être retourné.

En substance, cela pourrait être une opération qui est effectuée fréquemment, il est donc logique de ne pas reconstruire un tuple vide à chaque fois.


Lectures complémentaires:

Quelques autres réponses éclairent le comportement de mise en cache de CPython avec les immuables:

  • Pour les entiers, une autre réponse qui creuse dans la source peut être trouvée ici .
  • Pour les chaînes, une poignée de réponses peut être trouvée ici , ici et ici .
53