Comment fonctionne l'affectation avec la tranche de liste Python?

102

La documentation Python dit que le découpage d'une liste renvoie une nouvelle liste.
Maintenant, si une "nouvelle" liste est renvoyée, j'ai les questions suivantes liées à "Affectation aux tranches"

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

Maintenant, la sortie serait:

[4, 5, 3] 
  1. Comment quelque chose qui renvoie quelque chose peut-il venir du côté gauche de l'expression?
  2. Oui, j'ai lu la documentation et cela dit que c'est possible, maintenant que le découpage d'une liste renvoie une "nouvelle" liste, pourquoi la liste originale est-elle modifiée? Je ne suis pas en mesure de comprendre les mécanismes derrière cela.
Kartik Anand
la source
@Mark Longair désolé, je pensais que seul le code était censé être formaté et non la sortie
Kartik Anand
7
Comprenez-vous ce que a[0] = 4ferait?
Josh Lee
1
L'affectation @KartikAnand Slice est un scénario spécial dans lequel une nouvelle liste n'est pas créée. Cela n'a pas de sens de créer un objet sans liaison de nom sur le côté gauche d'un =, donc au lieu de le rejeter comme syntaxe invalide, python le transforme en quelque chose qui ressemble plus à ce à quoi vous pourriez vous attendre. Puisque python n'a pas de références, cela ne fonctionnerait pas pour que le résultat d'une tranche modifie la liste d'origine. Vous en obtenez une copie. Si vous avez fourni plus d'informations sur votre application, nous pourrons peut-être mieux vous aider à faire les choses de manière «pythonique». :)
Casey Kuball
1
@Darthfett Je ne travaille sur aucune application pour le moment, je m'apprends plutôt le python avant de commencer à me salir les mains :)
Kartik Anand

Réponses:

116

Vous confondez deux opérations distinctes qui utilisent une syntaxe très similaire:

1) tranchage:

b = a[0:2]

Cela fait une copie de la tranche de aet l'attribue à b.

2) affectation des tranches:

a[0:2] = b

Cela remplace la tranche de apar le contenu de b.

Bien que la syntaxe soit similaire (j'imagine par conception!), Ce sont deux opérations différentes.

NPE
la source
4
C'est ce que je doute, dans le second cas, pourquoi la tranche d'un, une nouvelle liste?
Kartik Anand
12
@KartikAnand Parce que ce n'est pas le cas. Ce n'est pas ce que spécifie la langue.
Marcin
Pour être clair, "prend une tranche de" signifie en réalité "faire une copie d'une tranche de" d'où vient une partie de la confusion.
Mark Ransom
2
@KartikAnand: En gros, oui. L'interprète sait qui est quoi et les gère de manière appropriée.
NPE
1
@Dubslow: vous pouvez le faire en utilisant le module itertools . Pour votre cas , utilisez la fonction iSlice , avec start=1, stop=None. Cela évitera toute copie et utilisera une évaluation paresseuse (dans votre cas, un accès paresseux à la liste d'origine).
Spiros
68

Lorsque vous spécifiez asur le côté gauche de l' =opérateur, vous utilisez l' affectation normale de Python , qui modifie le nom adans le contexte actuel pour pointer vers la nouvelle valeur. Cela ne change pas la valeur précédente vers laquelle apointait.

En spécifiant a[0:2]sur le côté gauche de l' =opérateur, vous indiquez à Python que vous souhaitez utiliser l' affectation de tranche . L'affectation de tranche est une syntaxe spéciale pour les listes, dans laquelle vous pouvez insérer, supprimer ou remplacer le contenu d'une liste:

Insertion :

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

Suppression :

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

Remplacement :

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

Remarque:

La longueur de la tranche peut être différente de la longueur de la séquence attribuée, modifiant ainsi la longueur de la séquence cible, si la séquence cible le permet. - source

L'affectation de tranche fournit une fonction similaire à la décompression de tuples . Par exemple,a[0:1] = [4, 5] équivaut à:

# Tuple Unpacking
a[0], a[1] = [4, 5]

Avec Tuple Unpacking, vous pouvez modifier des listes non séquentielles:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

Cependant, le déballage de tuple est limité au remplacement, car vous ne pouvez pas insérer ou supprimer des éléments.

Avant et après toutes ces opérations, ac'est la même liste exacte. Python fournit simplement un bon sucre syntaxique pour modifier une liste sur place.

Casey Kuball
la source
6
Similaire mais pas identique, car vous pouvez avoir un nombre différent d'éléments à gauche et à droite.
Mark Ransom
@MarkRansom C'est un excellent point, j'ai ajouté plus d'informations pour rendre cela évident.
Casey Kuball
2
Est-ce a[:] = some_listéquivalent à a = some_list[:]ou a = some_list?
jadkik94
2
@ jadkik94 Ni l'un ni l'autre. a[:] = some_listdéfinit chaque élément de acomme étant celui de some_list. Faire l'un ou l'autre de ceux que vous mentionnez changerait ce qui aest. Par exemple: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. La dernière ligne serait False si elle changeait ala valeur de, plutôt que de la muter.
Casey Kuball
@Darthfett Intéressant, j'avais trouvé le contraire :) Merci.
jadkik94
25

Je suis tombé sur la même question auparavant et elle est liée à la spécification du langage. Selon les déclarations de mission ,

  1. Si le côté gauche de l'affectation est un abonnement, Python appellera __setitem__ cet objet. a[i] = xéquivaut à a.__setitem__(i, x).

  2. Si le côté gauche de l'affectation est slice, Python appellera également __setitem__ , mais avec des arguments différents: a[1:4]=[1,2,3]équivaut à a.__setitem__(slice(1,4,None), [1,2,3])

C'est pourquoi la tranche de liste sur le côté gauche de '=' se comporte différemment.

Stan
la source
4

En découpant le côté gauche d'une opération d'affectation, vous spécifiez les éléments à affecter.

Fraxel
la source