En 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 est répétée 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 formidable si quelqu'un pouvait faire la lumière sur la raison pour laquelle cela se produit, tout au long de mon expérience Python, 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, cela n'est mentionné nulle part dans les documents.
l2 = tuple(iter(l1))
contourne l'optimisationPyTuple_GetSlice
été documenté de manière inexacte après avoir vu votre question. Les documents ont maintenant été corrigés (c'était le problème bpo38557 ).Réponses:
Les implémentations sont libres de renvoyer des instances identiques pour les types immuables (dans CPython, vous pouvez parfois voir des optimisations similaires pour les chaînes et les entiers). Étant donné que l'objet ne peut pas être modifié, il n'y a rien dans le code utilisateur qui doive se soucier s'il contient une instance unique ou simplement une autre référence à une instance existante.
Vous pouvez trouver le court-circuit dans le code C ici .
Il s'agit d'un détail d'implémentation, notez que pypy ne fait pas de même.
la source
a->ob_item
est de même(*a).ob_item
, c'est-à-dire qu'il obtient le membre appeléob_item
duPyTupleObject
pointé par a, et le + ilow avance ensuite au début de la tranche.C'est un détail d'implémentation. Étant donné que les listes sont modifiables, vous
l1[:]
devez créer une copie, car vous ne vous attendez pas à ce que les modifications soientl2
affectéesl1
.Étant donné qu'un tuple est immuable , cependant, vous ne pouvez rien faire
t2
qui affecteraitt1
de manière visible, le compilateur est donc libre (mais pas obligatoire ) d'utiliser le même objet pourt1
ett1[:]
.la source
En Python 3. *
my_list[:]
est un sucre syntaxique pourtype(my_list).__getitem__(mylist, slice_object)
où:slice_object
est un objet tranche construit à partirmy_list
des attributs (longueur) et de l'expression[:]
. Les objets qui se comportent de cette façon sont appelés indexables 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 bytecodeBINARY_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 à la même chosePyTupleObject
qu'il a obtenu comme argument d'entrée, si l'élément est de typePySlice
et la tranche est évaluée pour le tuple entier.Vous examinez maintenant le code
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é.la source
start:stop
tranche sur le type intégré, y compristup[:]
, ne passe pasBINARY_SUBSCR
. Le découpage étendustart:stop:step
passe par l'abonnement, cependant.Je n'en suis pas sûr, mais il semble que Python vous fournisse 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).
la source