web-dev-qa-db-fra.com

Le découpage de tuple ne renvoie pas un nouvel objet par opposition au découpage de liste

Dans Python (2 et 3). Chaque fois que nous utilisons le découpage de liste, il renvoie un nouvel objet, par exemple:

l1 = [1,2,3,4]
print(id(l1))
l2 = l1[:]
print(id(l2))

Production

>>> 140344378384464
>>> 140344378387272

Si la même chose se répète avec Tuple, le même objet est retourné, par exemple:

t1 = (1,2,3,4)
t2 = t1[:]
print(id(t1))
print(id(t2))

Production

>>> 140344379214896
>>> 140344379214896

Ce serait génial si quelqu'un pouvait faire la lumière sur la raison pour laquelle cela se produit, tout au long de mon Python expérience, j'avais l'impression que la tranche vide renvoie un nouvel objet.

Ma compréhension est qu'il retourne le même objet que les tuples sont immuables et il n'y a aucun intérêt d'en créer une nouvelle copie. Mais encore une fois, ce n'est mentionné nulle part dans les documents.

12
Vijay Jangir

Dans Python 3. * my_list[:] est le sucre syntaxique pour type(my_list).__getitem__(mylist, slice_object) où: slice_object est un objet tranche construit à partir des attributs (longueur) de my_list et de l'expression [:]. Objets qui se comportent de cette façon sont appelés indicibles dans le modèle de données Python voir ici . Pour les listes et les tuples, __getitem__ est une méthode intégrée.

En CPython, et pour les listes et les tuples, __getitem__ est interprété par l'opération de bytecode BINARY_SUBSCR qui est implémentée pour les tuples ici et pour les listes ici .

Dans le cas de tuples, en parcourant le code, vous verrez que dans ce bloc de code , static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item) renverra une référence au même PyTupleObject qu'il a obtenu en entrée , si l'élément est de type PySlice et que la tranche est évaluée à l'ensemble du tuple.

    static PyObject*
    tuplesubscript(PyTupleObject* self, PyObject* item)
    {
        /* checks if item is an index */ 
        if (PyIndex_Check(item)) { 
            ...
        }
        /* else it is a slice */ 
        else if (PySlice_Check(item)) { 
            ...
        /* unpacks the slice into start, stop and step */ 
        if (PySlice_Unpack(item, &start, &stop, &step) < 0) { 
            return NULL;
        }
       ...
        }
        /* if we start at 0, step by 1 and end by the end of the Tuple then !! look down */
        else if (start == 0 && step == 1 &&
                 slicelength == PyTuple_GET_SIZE(self) && 
                 PyTuple_CheckExact(self)) {
            Py_INCREF(self); /* increase the reference count for the Tuple */
            return (PyObject *)self; /* and return a reference to the same Tuple. */
        ...
}

Vous examinez maintenant le code de static PyObject * list_subscript(PyListObject* self, PyObject* item) et constatez par vous-même que quelle que soit la tranche, un nouvel objet liste est toujours renvoyé.

1
Fakher Mokadem

Je n'en suis pas sûr, mais il semble que Python vous fournit un nouveau pointeur vers le même objet pour éviter la copie car les tuples sont identiques (et puisque l'objet est un tuple, il est immuable).

0
michotross