2010-08-27 6 views
0

Je viens de démarrer Python et je suis tombé sur quelque chose d'étrange.Variables d'assignation permanentes Python

Le code suivant affecte une coordonnée de x = 1 et y = 2 au test de variable. La variable test2 s'attribue la même valeur que test, puis la valeur [x] pour test2 est remplacée par l'ancienne valeur [x] moins 1. Cela fonctionne bien, cependant, quand la dernière partie est exécutée, non seulement elle est moins 1 à partir de la valeur [x] dans test2, il fait la même chose avec la valeur [x] dans la variable de test.

test = [1,2]; 
test2 = test; 
test2[1] = test2[1] - 1; 

J'ai trouvé faire ce qui suit a bien fonctionné, mais je ne comprends toujours pas pourquoi la première méthode modifie la valeur de test ainsi que la valeur de test2.

test = [1,2]; 
test2 = test; 
test2 = [test2[0] -1 ,test2[1]]; 

Quelqu'un pourrait-il expliquer pourquoi cela se produit?

Merci Vous TheLorax

+0

Cette 'test2 [1] = test2 [1] - 1;' ne correspond pas à votre description. 'test2 [1]' est la valeur _y_ pour le tableau; la valeur _x_ serait 'test2 [0]'. –

+5

Astuce du pro: En Python les points-virgules sont inutiles et il est conventionnel de les omettre. :) – jathanism

Répondre

4

En Python, comme en Java, cession en soi ne fait jamais une copie - rahter, cession ajoute une autre référence au même objet que le côté droit de la =. (Le passage d'argument fonctionne de la même manière). Étrange que vous n'avez jamais entendu parler du concept, quand Python est très populaire et Java même beaucoup plus (et beaucoup d'autres langues tendent à fonctionner de manière similaire, éventuellement avec plus de complications ou distincos).

Lorsque vous voulez une copie, vous demandez une copie (dans le cadre de l'expression qui est sur le côté droit, ou est utilisé pour obtenir l'argument que vous êtes en passant)! En Python, en fonction de vos objectifs précis, il existe plusieurs façons - le module copy de la bibliothèque standard étant un populaire (avec les fonctions copy, pour les copies "peu profondes", et deepcopy - pour les copies "profondes", bien sûr, c'est-à-dire, ceux où, non seulement le conteneur, mais chaque élément contenu, est également copié en profondeur récursivement). Souvent, cependant, ce que vous voulez est "une nouvelle liste" - que la séquence d'origine soit une liste, une copie ou quelque chose d'autre qui soit itérable, cela ne vous intéresse pas: vous voulez une nouvelle instance list, dont sont les mêmes que ceux de "quelque chose d'autre". La meilleure façon d'exprimer cela est d'utiliser list(somethingelse) - appelez le type list, qui crée toujours une nouvelle instance de liste, et donnez-lui en argument le itérable dont vous voulez les éléments dans la nouvelle liste. De même, dict(somemapping) fait un nouveau dict, et set(someiterable) fait un nouvel ensemble - appeler un type pour faire une nouvelle instance de ce type est un concept très général et utile!

+0

Mais qu'en est-il de: 'a = 5',' b = a', 'a = 6',' assert (b == 6) '- échec d'assertion? Cela ne fait pas de référence; semble être une copie pour moi? –

+1

Encore une fois, cela est dû à la sémantique plutôt qu'à la sémantique d'affectation. Quand vous dites "b = a", vous liez le nom "b" à l'objet lié à "a", c'est-à-dire 5. Ensuite, quand vous dites "a = 6", vous êtes (re) lié le nom "a" à l'objet "6", mais le nom "b" reste lié à "5". – Jeet

+1

L'affectation est une forme de liaison de nom (les autres instructions de liaison de nom sont les clauses 'def',' class', 'import',' form', 'with' et' except' avec 'as', ...). Re-lier un nom de barre (c.-à-d., liant un nom de barre précédemment lié à autre chose) n'affecte jamais l'objet auquel ce nom était lié auparavant (ni ce qui est ou n'est pas lié aux autres noms). Appeler une ** méthode de mutation ** est bien sûr une question complètement différente; Les «variantes» d'assignation qui sont enrichies par les opérateurs ('+ =' & c), ou affectent les attributs d'un ou plusieurs objets dans un conteneur, sont également très différentes. –

3

C'est parce que quand vous faites test2 = test vous ne copiez pas le contenu de la liste, mais simplement attribuer test2 une référence à la liste initiale. Ainsi, toute modification apportée au test2 affectera également test.

La bonne façon de faire est d'utiliser deepcopy() à partir du module de copie (ou copy() si vous pouvez vous en sortir avec une copie superficielle.

import copy 
test2 = copy.deepcopy(test) # deep copy 

test2 = copy.copy(test)) # shallow copy 

test2 = test[:] # shallow copy using slices 

Voir this page pour une explication plus approfondie ainsi que d'autres méthodes pour copier une liste.

+0

Salut Ailyn, Merci pour votre réponse. C'est la première fois que je vois cela dans n'importe quelle langue. Existe-t-il un moyen de simplement copier le contenu sans les lier d'aucune façon? Merci TheLorax –

+0

@The_Lorax, oui, voir ma mise à jour – Aillyn

+1

@The_Lorax En fait, beaucoup de langues se comportent de cette façon. Le plus évident qui utilise toujours des copies est PHP. – Aillyn

0

[1,2] est un tableau et lorsque vous attribuez à test vous attribuez une référence à ce tableau pour tester. Alors, quand vous faites test2=test, vous assignez la référence à une autre variable. Modifiez toute variable et le tableau va être changé.

Votre deuxième exemple fonctionne, car vous créez un nouveau tableau tout à fait. Vous pourriez aussi bien l'avoir fait comme ceci:

test = [1,2] 
test2 = [test[0]-1, test[1]] 
0

Python ne fait pas d'affectation.

Il fait une liaison de nom.

Il existe une différence, et il est expliqué merveilleusement et en détail here.

Dans votre premier exemple: Votre première ligne lie le nom "test" à un objet de liste créé par l'expression "[1,2]". Votre deuxième ligne lie le nom "test2" au même objet qui est limité à "test". Votre troisième ligne mute l'objet lié à "test2", qui est le même que l'objet lié par "test".

Dans votre deuxième exemple: Votre première ligne lie le nom "test" à un objet de liste créé par l'expression "[1,2]". Votre deuxième ligne lie le nom "test2" au même objet qui est limité à "test". Votre troisième ligne lie le nom "test2" à un objet nouvel, créé par l'expression "[test2 [0] -1, test2 1]".

Si vous voulez vraiment deux listes indépendantes avec les mêmes valeurs liées à différentes variables, vous devez:

>>> test = [1, 2] 
>>> test2 = list(test) 
>>> test2[0] = 3 
>>> print(test) 
[1, 2] 
>>> print(test2) 
[3, 2] 

Notez que ceci est une liste peu profonde copie, la modification si l'état de l'objet d'un L'élément test2 affectera, bien sûr, test si le même objet est trouvé dans cette liste.

p.s. Python. Semi-colons. Laid. Inutile. Ne pas utiliser.

Questions connexes