web-dev-qa-db-fra.com

Comprendre * x, = lst

Je passe par un vieux code essayant de comprendre ce qu'il fait, et je suis tombé sur cette déclaration étrange:

*x ,= p

p est une liste dans ce contexte. J'ai essayé de comprendre ce que fait cette déclaration. Pour autant que je sache, il définit simplement x à la valeur de p. Par exemple:

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

Donne juste

[1, 2]

Est-ce différent de x = p? Une idée de ce que fait cette syntaxe?

68
Kewl

*x ,= p Est fondamentalement une version obscurcie de x = list(p) utilisant déballage itératif étend . La virgule après x est requise pour faire de la cible d'affectation un Tuple (il peut également s'agir d'une liste).

*x, = p est différent de x = p Car le premier crée une copie de p (c'est-à-dire une nouvelle liste) tandis que cette dernière crée un référence à la liste d'origine. Pour illustrer:

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

C'est une fonctionnalité qui a été introduite dans Python 3.0 ( PEP 3132 ). Dans Python 2, vous pouvez faire quelque chose comme ceci:

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

Python 3 a étendu cela afin 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. Au lieu de deux variables pour contenir trois valeurs, cependant, c'est juste une variable qui prend chaque valeur dans la liste. C'est différent de x = p car x = p signifie simplement que x est un autre nom pour p. Dans ce cas, cependant, c'est une nouvelle liste qui se trouve juste avoir les mêmes valeurs en elle. (Vous pourriez être intéressé par "Le moindre étonnement" et l'argument par défaut de Mutable )

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

>>> x = list(p)

et

>>> x = p[:]

Depuis Python 3.3, l'objet liste a en fait une méthode destinée à copier:

x = p.copy()

La tranche est en fait un concept très similaire. Comme l'a souligné nneonneo, cela ne fonctionne qu'avec des objets tels que des listes et des tuples qui prennent en charge les tranches. Cependant, la méthode que vous mentionnez fonctionne avec tous les itérables: dictionnaires, ensembles, générateurs, etc.

44
zondo

Vous devriez toujours les jeter à dis et voir ce qu'il vous renvoie; vous verrez en quoi *x, = p est réellement 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)

(Suppression des renvois None non liés)

Comme vous pouvez le voir, UNPACK_EX Est le code op différent entre ceux-ci; c'est documenté comme :

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

C'est pourquoi, comme l'a noté Eugene, vous obtenez un nouvel objet auquel le nom x fait référence et non une référence à un objet déjà existant (comme c'est le cas avec x = p).


*x, Semble très étrange (la virgule supplémentaire là-bas et tout) mais c'est obligatoire ici. Le côté gauche doit être un tuple ou une liste et, en raison de la bizarrerie de la création d'un tuple à élément unique en Python, vous devez utiliser un , De fin:

i = 1, # one element Tuple

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

[*x] = p

qui fait exactement la même chose mais n'a pas cette virgule supplémentaire qui traîne là-bas.

14