web-dev-qa-db-fra.com

Que se passe-t-il lorsque vous affectez la valeur d'une variable à une autre variable en Python?

C’est ma deuxième journée d’apprentissage python (je connais les bases du C++ et de la programmation orientée objet.) Et j’ai une légère confusion en ce qui concerne les variables en python.

Voici comment je les comprends actuellement:

Les variables Python sont des références (ou des pointeurs?) Aux objets (modifiables ou immuables). Lorsque nous avons quelque chose comme num = 5, l'objet immuable 5 est créé quelque part en mémoire et la paire de références nom-objet num est créée dans un certain espace de noms. Lorsque nous avons a = num, rien n'est copié, mais maintenant les deux variables font référence au même objet et a est ajouté au même espace de nom.

C'est là que mon livre, Automatiser les choses ennuyeuses avec Python, me déroute. Comme c'est un livre pour débutant, il ne mentionne pas les objets, les espaces de noms, etc., et tente d'expliquer le code suivant:

>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42

L'explication qu'il propose est exactement la même que celle d'un livre C++, ce qui ne me satisfait pas car nous traitons de références/pointeurs sur des objets. Donc, dans ce cas, je suppose que sur la 3ème ligne, les entiers étant immuables, spam se voit attribuer un tout nouveau pointeur/référence vers un emplacement différent en mémoire, c'est-à-dire que la mémoire à laquelle il était initialement destiné n'était pas '. t modifié. Nous avons donc cheese en référence à l'objet initial désigné par spam. Est-ce l'explication correcte?

73
Ruslan Mushkaev

En tant que développeur C++, vous pouvez considérer les variables Python comme des pointeurs.

Ainsi, lorsque vous écrivez spam = 100, cela signifie que vous "affectez le pointeur", qui pointait précédemment vers l'objet 42, pour qu'il pointe vers l'objet 100.

Auparavant, cheese était assigné pour pointer sur le même objet que spam pointé, qui se trouvait être 42 à ce moment-là. Puisque vous n'avez pas modifié cheese, il pointe toujours sur 42.

Dans ce cas, l’immuabilité n’a rien à voir, l’affectation de pointeur ne change rien à l’objet pointé.

79
Jonas Adler

La façon dont je vois les choses diffère selon les langues.

  • La perspective "juriste linguistique".
  • La perspective "programmeur pratique".
  • la perspective "implémenteur".

Du point de vue du juriste spécialiste des langues, python variables toujours "pointer sur" un objet. Cependant, contrairement à Java et à C++, le comportement de == <=> = etc dépend du type d'exécution des objets pointés par les variables. De plus, dans python, la gestion de la mémoire est gérée par le langage.

Du point de vue du programmeur pratique, nous pouvons traiter le fait que les entiers, les chaînes, les n-uplets, etc., sont des objets immuables * plutôt que des valeurs droites comme un détail non pertinent. La seule exception est lorsque vous stockez de grandes quantités de données numériques, nous souhaitons peut-être utiliser des types capables de stocker les valeurs directement (par exemple, tableaux numpy) plutôt que des types qui aboutiront à un tableau contenant de nombreuses références à de minuscules objets.

Du point de vue des implémenteurs, la plupart des langages ont une sorte de règle as-if telle que si les comportements spécifiés sont corrects, la mise en œuvre est correcte quelle que soit la façon dont les choses se passent réellement.

Alors oui, votre explication est correcte du point de vue d'un juriste spécialiste des langues. Votre livre est correct du point de vue d'un programmeur pratique. Ce qu'une implémentation fait réellement dépend de l'implémentation. Dans cpython, les entiers sont des objets réels, bien que les entiers de faible valeur proviennent d'un pool de cache et ne soient pas créés à nouveau. Je ne suis pas sûr de ce que font les autres implémentations (pypy et jython, par exemple).

* notez ici la distinction entre les objets mutables et immuables. Avec un objet mutable, nous devons faire attention à le traiter "comme une valeur" car un autre code pourrait le transformer. Avec un objet immuable, nous n'avons pas de telles préoccupations.

20
plugwash

Il est vrai que vous pouvez plus ou moins utiliser des variables comme pointeurs. Cependant, un exemple de code aiderait beaucoup à expliquer comment cela fonctionne réellement.

Premièrement, nous utiliserons beaucoup la fonction id :

Renvoie "l'identité" d'un objet. C'est un entier qui est garanti d'être unique et constant pour cet objet tout au long de sa vie. Deux objets dont la durée de vie ne se chevauchent pas peuvent avoir la même valeur id ().

Il est probable que cela renvoie différentes valeurs absolues sur votre ordinateur.

Considérons cet exemple:

>>> foo = 'a string'
>>> id(foo) 
4565302640
>>> bar = 'a different string'
>>> id(bar)
4565321816
>>> bar = foo
>>> id(bar) == id(foo)
True
>>> id(bar)
4565302640

Tu peux voir ça:

  • Le foo/bar original a des identifiants différents, car ils pointent vers des objets différents
  • Lorsque bar est assigné à foo, leurs identifiants sont maintenant les mêmes. Ceci est similaire aux deux qui pointent vers le même emplacement en mémoire que vous voyez en faisant un pointeur C++

lorsque nous changeons la valeur de foo, elle est assignée à un autre identifiant:

>>> foo = 42
>>> id(foo)
4561661488
>>> foo = 'oh no'
>>> id(foo)
4565257832

Une observation intéressante est aussi que les entiers ont implicitement cette fonctionnalité jusqu'à 256:

>>> a = 100
>>> b = 100
>>> c = 100
>>> id(a) == id(b) == id(c)
True

Cependant, au-delà de 256, ce n'est plus vrai:

>>> a = 256
>>> b = 256
>>> id(a) == id(b)
True
>>> a = 257
>>> b = 257
>>> id(a) == id(b)
False

cependant, assigner a à b conservera l'identifiant identique à celui indiqué précédemment:

>>> a = b
>>> id(a) == id(b)
True
19
enderland

Python n'est ni passe-à-référence ni passe-à-valeur. Python les variables ne sont pas des pointeurs, ni des références, ni des valeurs. Les variables Python sont des noms .

Considérez-le comme "passe-par-alias" si vous avez besoin du même type de phrase, ou éventuellement "passe-par-objet", car vous pouvez faire muter le même objet à partir de toute variable qui l'indique, s'il est mutable, mais la réaffectation de une variable (alias) ne change que cette variable.

Si cela aide: Les variables C sont des boîtes dans lesquelles vous écrivez des valeurs. Python les noms sont des balises que vous mettez sur des valeurs.

Le nom d'une variable Python est une clé de l'espace de noms global (ou local), qui est en réalité un dictionnaire. La valeur sous-jacente est un objet en mémoire. L'affectation donne un nom à cet objet. L'affectation d'une variable à une autre variable signifie que les deux variables sont des noms pour le même objet. La réaffectation d'une variable modifie l'objet qui est nommé par cette variable sans modifier l'autre variable. Vous avez déplacé la balise mais n'avez pas modifié l'objet précédent ni aucune autre balise dessus.

Dans le code C sous-jacent de l’implémentation CPython, chaque objet Python est un PyObject*, vous pouvez donc l’imaginer comme si vous n’aviez que des pointeurs sur des données (aucun pointeur sur -pointers, pas de valeurs transmises directement).

vous pouvez dire que Python est un paramètre valeur par passe, où les valeurs sont des pointeurs… ou vous pouvez dire que Python est un système référencé, où les références sont des copies.

16
David Heyman

Lorsque vous exécutez spam = 100 python, créez un autre objet dans la mémoire, mais ne modifiez pas l'existant. donc vous avez toujours le pointeur cheese à 42 et spam à 100

10
Oleksandr Dashkov

Comme @DeepSpace l'a mentionné dans les commentaires, Ned Batchelder fait un excellent travail en démystifiant les variables (noms) et les assignations de valeurs dans un blog, à partir duquel il a prononcé un discours à PyCon 2015, Faits et mythes sur Python noms et valeurs . Cela peut être intéressant pour les pythonistes à n'importe quel niveau de maîtrise.

8
pylang

Ce qui se passe dans la ligne spam = 100 remplace la valeur précédente (pointeur sur un objet de type int avec la valeur 42) par un autre pointeur sur un autre objet (type int, valeur 100)

8
bakatrouble

Lorsque vous stockez spam = 42, un objet est créé dans la mémoire. Ensuite, vous affectez cheese = spam, Il attribue l'objet référencé par spam à cheese. Et enfin, lorsque vous modifiez spam = 100, il ne modifie que l'objet spam. Donc, cheese = 42.

1
Md. Rezwanul Haque