Python attribuant plusieurs variables à la même valeur? comportement de liste

132

J'ai essayé d'utiliser plusieurs affectations comme indiqué ci-dessous pour initialiser les variables, mais j'ai été confus par le comportement, je m'attends à réaffecter la liste de valeurs séparément, je veux dire que b [0] et c [0] égalent 0 comme avant.

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

Le résultat est: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Est-ce exact? que dois-je utiliser pour les affectations multiples? qu'est-ce qui est différent de cela?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

résultat: ('f:', 3) ('e:', 4)

Marco
la source
3
Est - ce que vous voulez a, bet c,à tous les points à la même valeur (dans ce cas , une liste), ou voulez - vous a=0, b=3et c=5. Dans ce cas, vous voulez a,b,c = [0,3,5]ou tout simplement a,b,c = 0,3,5.
chepner

Réponses:

271

Si vous arrivez à Python à partir d'un langage du C / Java / etc. famille, cela peut vous aider à arrêter de penser à aune «variable» et à commencer à y penser comme un «nom».

a, bet cne sont pas des variables différentes avec des valeurs égales; ce sont des noms différents pour la même valeur identique. Les variables ont des types, des identités, des adresses et toutes sortes de choses comme ça.

Les noms n'ont rien de tout cela. Les valeurs le font, bien sûr, et vous pouvez avoir beaucoup de noms pour la même valeur.

Si vous offrez Notorious B.I.G.un hot-dog * Biggie Smallset Chris Wallaceun hot-dog. Si vous remplacez le premier élément de apar 1, les premiers éléments de bet csont 1.

Si vous voulez savoir si deux noms nomment le même objet, utilisez l' isopérateur:

>>> a=b=c=[0,3,5]
>>> a is b
True

Vous demandez alors:

qu'est-ce qui est différent de cela?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Ici, vous reliez le nom eà la valeur 4. Cela ne touche pas les noms det fen aucune façon.

Dans votre version précédente, vous attribuiez à a[0], pas à a. Donc, du point de vue de a[0], vous reliez a[0], mais du point de vue de a, vous le modifiez sur place.

Vous pouvez utiliser la idfonction, qui vous donne un numéro unique représentant l'identité d'un objet, pour voir exactement quel objet est lequel même si iscela ne peut pas aider:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Notez que cela a[0]est passé de 4297261120 à 4297261216 - c'est maintenant un nom pour une valeur différente. Et b[0]est également maintenant un nom pour cette même nouvelle valeur. C'est parce que aet bcontinuent de nommer le même objet.


Sous les couvertures, a[0]=1appelle en fait une méthode sur l'objet de liste. (C'est équivalent à a.__setitem__(0, 1).) Donc, ce n'est vraiment rien du tout. C'est comme appeler my_object.set_something(1). Bien sûr, l'objet est probablement en train de relier un attribut d'instance afin d'implémenter cette méthode, mais ce n'est pas ce qui est important; ce qui est important, c'est que vous n'attribuez rien, vous faites simplement muter l'objet. Et c'est la même chose avec a[0]=1.


user570826 a demandé:

Et si nous avons, a = b = c = 10

C'est exactement la même situation que a = b = c = [1, 2, 3]: vous avez trois noms pour la même valeur.

Mais dans ce cas, la valeur est an intet ints sont immuables. Dans les deux cas, vous pouvez associer aà une valeur différente (par exemple a = "Now I'm a string!"), mais n'affectera pas la valeur initiale, qui bet csera toujours des noms pour. La différence est qu'avec une liste, vous pouvez changer la valeur [1, 2, 3]en [1, 2, 3, 4]faisant, par exemple a.append(4),; puisque cela change en fait la valeur pour laquelle bet csont des noms, bva maintenant b [1, 2, 3, 4]. Il n'y a aucun moyen de changer la valeur 10en autre chose. 10a 10 pour toujours, tout comme Claudia la vampire a 5 pour toujours (du moins jusqu'à ce qu'elle soit remplacée par Kirsten Dunst).


* Attention: ne donnez pas à Notorious BIG un hot-dog. Les zombies de gangsta rap ne devraient jamais être nourris après minuit.

Abarnert
la source
Et si nous have, a = b = c = 10;et quand nous essayons de mettre à jour la valeur de b, cela affecte tout autre? bien que j'ai vérifié leurs identifiants sont les mêmes.?
AJ
@ user570826: 10est immuable - cela signifie qu'il n'y a aucun moyen de mettre à jour la valeur, donc votre question n'a pas de sens. Vous pouvez pointer bsur une valeur différente, mais cela n'a aucun effet sur aet c, qui pointent toujours vers la valeur d'origine. La différence qu'apportent les listes est qu'elles sont modifiables. Par exemple, vous pouvez créer appendune liste oulst[0] = 3 , et cela mettra à jour la valeur, qui sera visible à travers tous les noms de cette valeur.
abarnert
72

Tousse tousse

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 
Jimmy Kane
la source
10
À mon humble avis, cela répond en fait à la première question clé de l'OP: que dois-je utiliser pour les affectations multiples, alors que la réponse la plus élevée et la plus cérébrale ci-dessus ne le fait pas.
Will Croxford
2
Ou a,b,c = 1,2,3sans crochets fonctionne en Python 2 ou 3, si vous voulez vraiment ce cm supplémentaire de lisibilité.
Will Croxford
14

Oui, c'est le comportement attendu. a, b et c sont tous définis comme étiquettes pour la même liste. Si vous voulez trois listes différentes, vous devez les attribuer individuellement. Vous pouvez soit répéter la liste explicite, soit utiliser l'une des nombreuses façons de copier une liste:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Les instructions d'affectation en Python ne copient pas les objets - elles lient le nom à un objet et un objet peut avoir autant d'étiquettes que vous en définissez. Lors de votre première modification, en changeant un [0], vous mettez à jour un élément de la liste unique auquel a, b et c font tous référence. Dans votre deuxième, en changeant e, vous changez e pour être une étiquette pour un objet différent (4 au lieu de 3).

Peter DeGlopper
la source
13

En python, tout est un objet, aussi des types de variables "simples" (int, float, etc.).

Lorsque vous modifiez la valeur d'une variable, vous modifiez en fait son pointeur , et si vous comparez entre deux variables, il compare leurs pointeurs . (Pour être clair, le pointeur est l'adresse dans la mémoire physique de l'ordinateur où une variable est stockée).

Par conséquent, lorsque vous modifiez une valeur de variable interne, vous modifiez sa valeur dans la mémoire et cela affecte toutes les variables qui pointent vers cette adresse.

Pour votre exemple, lorsque vous faites:

a = b =  5 

Cela signifie que a et b pointe vers la même adresse en mémoire qui contient la valeur 5, mais lorsque vous faites:

a = 6

Cela n'affecte pas b car a est maintenant pointe vers un autre emplacement mémoire qui contient 6 et b pointe toujours vers l'adresse mémoire qui contient 5.

Mais, quand vous faites:

a = b = [1,2,3]

a et b, encore une fois, indiquent le même emplacement, mais la différence est que si vous modifiez l'une des valeurs de la liste:

a[0] = 2

Cela change la valeur de la mémoire sur laquelle pointe a is, mais a is pointe toujours vers la même adresse que b, et par conséquent, b change également.

Ori Seri
la source
6
Ceci est très trompeur. Les pointeurs ne sont certainement pas visibles au niveau Python, et au moins deux des quatre implémentations majeures (PyPy et Jython) ne les utilisent pas même dans l'implémentation.
abarnert
1
Vous êtes invités à lire et à explorer les composants internes de python et vous découvrirez que chaque variable en python est en fait un pointeur.
Ori Seri
4
Non. Dans une implémentation de Python (CPython), chaque variable est un pointeur vers un fichier PyObject. Ce n'est pas vrai dans d'autres implémentations comme PyPy ou Jython. (En fait, on ne sait même pas comment cela pourrait être vrai, car les langages dans lesquels ces implémentations sont écrites n'ont même pas de pointeurs.)
abarnert
Je pense que l'utilisation de "pointeur" dans un sens conceptuel est acceptable (peut-être avec une clause de non-responsabilité selon laquelle les implémentations peuvent varier), surtout si l'objectif est de transmettre un comportement.
Levon
@abarnert Quand les gens disent Python, ils parlent de CPython, pas d'autres implémentations rarement utilisées. Tout comme quand les gens disent Kleenex, ils parlent de mouchoirs en papier. Jouer au jeu de la sémantique dans ces commentaires est vraiment inutile. Quant à ce qu'il a écrit, le comportement de ce qu'il a décrit est-il mauvais?
swade
10

Vous pouvez utiliser id(name)pour vérifier si deux noms représentent le même objet:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Les listes sont modifiables; cela signifie que vous pouvez modifier la valeur en place sans créer un nouvel objet. Cependant, cela dépend de la façon dont vous modifiez la valeur:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Si vous attribuez une nouvelle liste a, puis son id va changer, donc il ne sera pas affecter bet cde valeurs:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Les entiers sont immuables, vous ne pouvez donc pas modifier la valeur sans créer un nouvel objet:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
Jurgenreza
la source
1
idn'est pas nécessairement un emplacement mémoire. Comme le disent les documents , cela renvoie «l'identité… un entier… qui est garanti unique et constant pour cet objet pendant sa durée de vie». CPython utilise l'adresse mémoire comme adresse id, mais d'autres implémentations Python peuvent ne pas le faire. PyPy, par exemple, ne le fait pas. Et dire "deux vars pointent vers le même emplacement mémoire" est trompeur pour quiconque comprend le style C. «Deux noms pour le même objet» est à la fois plus précis et moins trompeur.
abarnert
@abarnert merci pour la clarification, j'ai mis à jour la réponse.
jurgenreza
7

dans votre premier exemple, a = b = c = [1, 2, 3]vous dites vraiment:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Si vous voulez définir 'a' égal à 1, 'b' égal à '2' et 'c' égal à 3, essayez ceci:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

J'espère que cela t'aides!

Nick Burns
la source
4

En termes simples, dans le premier cas, vous attribuez plusieurs noms à un list . Une seule copie de la liste est créée en mémoire et tous les noms font référence à cet emplacement. Ainsi, changer la liste en utilisant l'un des noms modifiera en fait la liste en mémoire.

Dans le second cas, plusieurs copies de même valeur sont créées en mémoire. Ainsi, chaque copie est indépendante l'une de l'autre.

Vikas
la source
3

Voici ce dont vous avez besoin:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
pydsigner
la source
3

Le code qui fait ce dont j'ai besoin pourrait être le suivant:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Résultat:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Marco
la source