80 votes

Comprendre *x ,= lst

Je suis en train de parcourir un vieux code pour essayer de comprendre ce qu'il fait, et je suis tombé sur cette étrange déclaration :

*x ,= p

p est une liste dans ce contexte. J'ai essayé de comprendre ce que fait cette instruction. D'après ce que je sais, elle place simplement x à la valeur de p . Par exemple :

p = [1,2]
*x ,= p    
print(x)

Donne juste

[1, 2]

Alors est-ce que c'est différent de x = p ? Une idée de ce que fait cette syntaxe ?

2 votes

C'est différent car au lieu d'attribuer un alias, il copie la liste.

0 votes

4 votes

L'omission de la virgule donne un message d'erreur auquel ceci pourrait être une référence intéressante : "SyntaxError : starred assignment target must be in a list or tuple".

86voto

eugene y Points 37378

*x ,= p est essentiellement une version obfusquée de x = list(p) en utilisant déballage étendu des itérables . La virgule après x est nécessaire pour que la cible de l'affectation soit un tuple (cela peut aussi être une liste).

*x, = p est différent de x = p car le premier crée un copie de p (c'est-à-dire une nouvelle liste) tandis que la seconde crée une référence à la liste originale. Pour illustrer :

>>> p = [1, 2]
>>> *x, = p 
>>> x == p
True
>>> x is p
False
>>> x = p
>>> x == p
True
>>> x is p
True

16 votes

Il convient également de noter que la virgule appartient en fait à l'élément *x car l'affectation étoilée doit être dans une liste ou un tuple. La manière la plus explicite d'écrire cette instruction est donc la suivante (*x,) = p

3 votes

Il est important de noter que cela fonctionne lorsque p est tout itérable.

1 votes

Il pourrait être utile d'ajouter quelque chose comme x[0] = 3; p #=> [1, 2] à la première mi-temps et x[0] = 3; p #=> [3, 2] au second, pour illustrer pourquoi is et == sont différents

51voto

zondo Points 11370

Il s'agit d'une fonctionnalité qui a été introduite dans Python 3.0 ( PEP 3132 ). En Python 2, vous pouviez faire quelque chose comme ceci :

>>> p = [1, 2, 3]
>>> q, r, s = p
>>> q
1
>>> r
2
>>> s
3

Python 3 a étendu ce principe de sorte qu'une variable puisse contenir plusieurs valeurs :

>>> p = [1, 2, 3]
>>> q, *r = p
>>> q
1
>>> r
[2, 3]

C'est donc ce qui est utilisé ici. Cependant, au lieu de deux variables pour contenir trois valeurs, c'est une seule variable qui prend chaque valeur de la liste. Ceci est différent de x = p parce que x = p signifie simplement que x est un autre nom pour p . Dans ce cas, cependant, il s'agit d'une nouvelle liste qui contient justement les mêmes valeurs. (Vous pouvez vous intéresser à Le "moindre étonnement" et l'argument du défaut mutable )

Deux autres façons courantes de produire cet effet sont :

>>> x = list(p)

et

>>> x = p[:]

Depuis Python 3.3, l'objet liste possède en fait une méthode destinée à la copie :

x = p.copy()

La tranche est en fait un concept très similaire. Comme l'a souligné nneonneo, cependant, cela ne fonctionne qu'avec des objets tels que les listes et les tuples qui supportent les tranches. La méthode que vous mentionnez, en revanche, fonctionne avec n'importe quel itérable : dictionnaires, ensembles, générateurs, etc.

0 votes

Notez, cependant, que votre deuxième bout de code, x = p[:] exige que p être tranchable. Cela exclut les choses comme les générateurs.

1 votes

Attends, 3.0 !? Je pensais que c'était beaucoup plus récent, 3.5 ou 3.6 ou quelque chose comme ça.

2 votes

@user2357112 : En Nouveautés de Python 3.0 vous trouverez ce point : PEP 3132 was accepted . En outre, le PEP lui-même indique que la version de Python est la 3.0. Je suis donc presque sûr que ce doit être le cas.

16voto

Jim Points 8793

Vous devriez toujours les jeter à dis et voyez ce qu'il vous renvoie ; vous verrez comment *x, = p est en fait différent de x = p :

dis('*x, = p')
  1           0 LOAD_NAME                0 (p)
              2 UNPACK_EX                0
              4 STORE_NAME               1 (x)

Alors que, la simple déclaration d'affectation :

dis('x = p')
  1           0 LOAD_NAME                0 (p)
              2 STORE_NAME               1 (x)

(Dépouillement d'informations non liées None retours)

Comme vous pouvez le constater UNPACK_EX est la différence de code d'opération entre eux ; il est documenté comme :

Implémente l'affectation avec une cible étoilée : Défait un itérable en TOS (top of stack) en valeurs individuelles, où le nombre total de valeurs peut être inférieur au nombre d'éléments dans l'itérable : une des nouvelles valeurs sera une liste de tous les éléments restants.

C'est pourquoi, comme l'a noté Eugène, vous obtenez un nouvel objet auquel on se réfère par le nom x et non une référence à un objet déjà existant (comme c'est le cas avec l'option x = p ).


*x, semble très étrange (la virgule supplémentaire et tout le reste) mais elle est nécessaire ici. Le côté gauche doit être soit un tuple, soit une liste et, en raison de la bizarrerie de la création d'un tuple à élément unique en Python, vous devez utiliser un caractère de queue , :

i = 1, # one element tuple

Si vous aimez embrouiller les gens, vous pouvez toujours utiliser la fonction list version de ceci :

[*x] = p

qui fait exactement la même chose, mais sans la virgule supplémentaire qui traîne.

4voto

Ravi G Points 492

Vous pouvez le comprendre clairement à partir de l'exemple ci-dessous

L = [1, 2, 3, 4]
while L:
    temp, *L = L             
    print(temp, L)

ce qu'il fait, c'est que la variable frontale obtiendra le premier élément à chaque fois et la liste restante sera donnée à L.

Le résultat sera le suivant.

1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []

Regardez également l'exemple ci-dessous

x, *y, z = "python"
print(x,y,z)

Dans ce cas, x et z obtiendront chacun une lettre de la chaîne, c'est-à-dire que la première lettre sera attribuée à x et la dernière lettre sera attribuée à z et la chaîne restante sera attribuée à la variable y.

p ['y', 't', 'h', 'o'] n

Un autre exemple,

a, b, *c = [0,1,2,3]
print(a,b,c)

0 1 [2,3]

Cas limite : S'il ne reste rien pour la variable étoile, elle obtiendra une liste vide.

Exemple :

a,b=[1]
print(a,b)

1 []

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X