Pourquoi l'affectation à une liste vide (par exemple [] = "") n'est-elle pas une erreur?

110

En python 3.4, je tape

[] = "" 

et cela fonctionne bien, aucune exception n'est soulevée. Bien sûr, ce []n'est pas égal à ""après.

[] = ()

fonctionne également très bien.

"" = []

lève une exception comme prévu,

() = ""

lève une exception comme prévu. Alors que se passe-t-il?

Vardd
la source

Réponses:

132

Vous ne comparez pas pour l'égalité. Vous attribuez .

Python vous permet d'attribuer à plusieurs cibles:

foo, bar = 1, 2

affecte les deux valeurs à fooet bar, respectivement. Tout ce dont vous avez besoin est une séquence ou une itération sur le côté droit, et une liste ou un tuple de noms sur la gauche.

Quand vous faites:

[] = ""

vous avez affecté une séquence vide (les chaînes vides sont toujours des séquences) à une liste vide de noms.

C'est essentiellement la même chose que de faire:

[foo, bar, baz] = "abc"

où vous vous retrouvez avec foo = "a", bar = "b"et baz = "c", mais avec moins de caractères.

Cependant, vous ne pouvez pas attribuer à une chaîne, donc ""sur le côté gauche d'une affectation ne fonctionne jamais et est toujours une erreur de syntaxe.

Consultez la documentation des instructions d'affectation :

Une instruction d'affectation évalue la liste d'expressions (rappelez-vous qu'il peut s'agir d'une seule expression ou d'une liste séparée par des virgules, cette dernière donnant un tuple) et attribue le seul objet résultant à chacune des listes cibles, de gauche à droite.

et

L'affectation d'un objet à une liste cible, éventuellement entre parenthèses ou crochets , est définie de manière récursive comme suit.

Soulignez le mien .

Que Python ne lève pas d'erreur de syntaxe pour la liste vide est en fait un peu un bogue! La grammaire officiellement documentée ne permet pas une liste cible vide, et pour le vide, ()vous obtenez une erreur. Voir le bogue 23275 ; il est considéré comme un bug inoffensif:

Le point de départ est de reconnaître que cela existe depuis très longtemps et est inoffensif.

Voir aussi Pourquoi est-il valide d'assigner à une liste vide mais pas à un tuple vide?

Martijn Pieters
la source
36

Il suit les règles de la section Déclarations d' affectation de la documentation,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Si le target listest une liste de cibles séparées par des virgules: L'objet doit être un itérable avec le même nombre d'éléments qu'il y a de cibles dans la liste de cibles, et les éléments sont affectés, de gauche à droite, aux cibles correspondantes.

L'objet doit être une séquence avec le même nombre d'éléments qu'il y a de cibles dans la liste cible, et les éléments sont affectés, de gauche à droite, aux cibles correspondantes.

Alors, quand tu dis

[] = ""

"" est un itérable (toute chaîne python valide est un itérable) et il est décompressé sur les éléments de la liste.

Par exemple,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Puisque vous avez une chaîne vide et une liste vide, il n'y a rien à décompresser. Donc, pas d'erreur.

Mais essayez ceci

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

Dans ce [] = "1"cas, vous essayez de décompresser la chaîne "1"sur une liste vide de variables. Donc, il se plaint de "trop ​​de valeurs à décompresser (0 attendu)".

De la même manière, au [a] = ""cas où vous auriez une chaîne vide, donc rien à décompresser vraiment, mais vous la décompressez sur une variable, ce qui, encore une fois, n'est pas possible. C'est pourquoi il se plaint "d'avoir besoin de plus de 0 valeurs pour décompresser".

En dehors de cela, comme vous l'avez remarqué,

>>> [] = ()

ne jette également aucune erreur, car ()est un tuple vide.

>>> ()
()
>>> type(())
<class 'tuple'>

et quand il est décompressé sur une liste vide, il n'y a rien à décompresser. Donc pas d'erreur.


Mais, quand tu fais

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

comme le message d'erreur l'indique, vous essayez d'affecter à une chaîne littérale. Ce qui n'est pas possible. C'est pourquoi vous obtenez les erreurs. C'est comme dire

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internes

En interne, cette opération d'affectation sera traduite en UNPACK_SEQUENCEcode opération,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Ici, puisque la chaîne est vide, UNPACK_SEQUENCEdécompresse les 0temps. Mais quand tu as quelque chose comme ça

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

la séquence 123est décompressée dans la pile, de droite à gauche. Ainsi, le haut de la pile serait 1et le suivant serait 2et le dernier serait 3. Ensuite, il affecte à partir du haut de la pile les variables de l'expression de gauche une par une.


BTW, en Python, voici comment vous pouvez faire plusieurs affectations dans la même expression. Par exemple,

a, b, c, d, e, f = u, v, w, x, y, z

cela fonctionne parce que les valeurs de droite sont utilisées pour construire un tuple, puis il sera décompressé sur les valeurs de gauche.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

mais la technique d'échange classique a, b = b, autilise la rotation des éléments en haut de la pile. Si vous n'avez que deux ou trois éléments, ils sont traités avec des instructions spéciales ROT_TWOet ROT_THREEau lieu de construire le tuple et de le décompresser.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
thefourtheye
la source
Vous pouvez également utiliser dis('[] = ""')sans appeler compile().
Andrea Corbellini
Pouvez-vous décrire ce qui se passe si vous échangez plus de trois variables / éléments, en utilisant la méthode de votre dernier exemple?
nanofarad
@hexafraction Il construira un nouveau tuple avec tous les éléments du côté droit, puis il les décompressera sur les variables du côté gauche.
thefourtheye