94 votes

Les variables Python sont-elles des pointeurs ? Ou encore, que sont-elles ?

Les variables en Python sont juste des pointeurs, pour autant que je sache.

Sur la base de cette règle, je peux supposer que le résultat pour cet extrait de code :

i = 5
j = i
j = 3
print(i)

serait 3 .

Mais j'ai obtenu un résultat inattendu pour moi, et c'était 5 .

D'ailleurs, mon livre Python traite de cet exemple :

i = [1,2,3]
j = i
i[0] = 5
print(j)

Le résultat serait [5,2,3] .

Qu'est-ce que je comprends mal ?

99voto

gnibbler Points 103484

Nous les appelons des références. Elles fonctionnent comme suit

i = 5     # create int(5) instance, bind it to i
j = i     # bind j to the same int as i
j = 3     # create int(3) instance, bind it to j
print i   # i still bound to the int(5), j bound to the int(3)

Les petits ints sont internés, mais ce n'est pas important pour cette explication.

i = [1,2,3]   # create the list instance, and bind it to i
j = i         # bind j to the same list as i
i[0] = 5      # change the first item of i
print j       # j is still bound to the same list as i

35voto

Mark Byers Points 318575

Les variables ne sont pas des pointeurs. Lorsque vous assignez à une variable, vous liaison le nom d'un objet. À partir de ce moment, vous pouvez faire référence à l'objet en utilisant le nom, jusqu'à ce que ce nom soit rebondi.

Dans votre premier exemple, le nom i est lié à la valeur 5 . Lier différentes valeurs au nom j n'a pas d'effet sur i Ainsi, lorsque vous imprimerez plus tard la valeur de i la valeur est toujours 5 .

Dans votre deuxième exemple, vous liez les deux i y j à la même objet de la liste. Lorsque vous modifiez le contenu de la liste, vous pouvez voir le changement quel que soit le nom que vous utilisez pour faire référence à la liste.

Notez que ce serait incorrect si vous disiez "les deux listes ont changé". Il n'y a qu'une seule liste mais elle a deux noms ( i y j ) qui y font référence.

Documentation connexe

17voto

Aaron Hall Points 7381

Les variables Python sont des noms liés à des objets

De la docs :

Les noms font référence à des objets. Les noms sont introduits par des opérations de liaison de noms. Chaque occurrence d'un nom dans le texte du programme fait référence à la liaison de ce nom. établi dans le bloc fonctionnel le plus interne contenant l'utilisation.

Quand vous le faites

i = 5
j = i

c'est la même chose que de faire :

i = 5
j = 5

j n'indique pas i et après la mission, j ne sait pas que i existe. j est simplement lié à ce que i pointait au moment de l'affectation.

Si vous faisiez les affectations sur la même ligne, cela ressemblerait à ceci :

i = j = 5

Et le résultat serait exactement le même.

Ainsi, en faisant plus tard

i = 3

ne change pas ce que j pointe vers - et vous pouvez le remplacer - j = 3 ne changerait pas ce que i pointe vers.

Votre exemple ne supprime pas la référence à la liste

Donc quand tu fais ça :

i = [1,2,3]
j = i

C'est la même chose que de faire ça :

i = j = [1,2,3]

donc i y j pointent tous deux vers la même liste. Ensuite, votre exemple fait muter la liste :

i[0] = 5

Les listes Python sont des objets mutables, donc lorsque vous modifiez la liste à partir d'une référence, et que vous la regardez à partir d'une autre référence, vous verrez le même résultat car il s'agit de la même liste.

Ce que vous voulez probablement, c'est une copie de la liste, comme ceci peut-être :

i = [1,2,3]
j = i.copy()

Notez que les deux listes contiennent les mêmes objets et que s'ils sont mutables, ils seront dans le même état muté lorsqu'on y accède à partir des deux listes, car ce sont les mêmes objets.

16voto

MisterMiyagi Points 2734

TLDR : Python noms fonctionnent comme des pointeurs avec dé/référencement automatique mais ne permettent pas d'opérations explicites sur les pointeurs. D'autres cibles représentent des indirections, qui se comportent comme des pointeurs.


La spécification du langage Python ne définit pas ce que sont les noms et autres éléments de ce type sont seulement comment ils se comportent. Toutefois, le comportement peut être expliqué à l'aide de pointeurs.

L'implémentation de CPython utilise pointeurs de type PyObject* sous le capot. En tant que tel, il est possible de traduire la sémantique des noms en opérations sur les pointeurs. La clé est de séparer noms de la réalité objets .

L'exemple de code Python inclut les deux noms ( i ) et des objets ( 5 ).

i = 5  # name `i` refers to object `5`
j = i  # ???
j = 3  # name `j` refers to object `3`

Cela peut être traduit grossièrement en code C avec séparé les noms et les objets.

int three=3, five=5;  // objects
int *i, *j;           // names
i = &five;   // name `i` refers to position of object `5`
j = i;       // name `j` refers to referent of `i`
j = &three;  // name `j` refers to position of object `3`

L'important est que les "noms en tant que pointeurs" ne stockent pas d'objets ! Nous n'avons pas défini *i = five mais i = &five . Les noms et les objets existent indépendamment les uns des autres.

Noms seulement pointer vers les objets existants en mémoire.

Lors d'une affectation de nom à nom, aucun objet n'est échangé ! Lorsque nous définissons j = i ce qui équivaut à j = &five . Ni l'un ni l'autre i ni j sont connectés les uns aux autres.

+- name i -+ -\
               \
                --> + <five> -+
               /    |        5 |
+- name j -+ -/     +----------+

En conséquence, le changement de la cible d'un nom n'affecte pas l'autre . Il ne met à jour que ce vers quoi pointe ce nom spécifique.


Python possède également d'autres types d'éléments ressemblant à des noms : références d'attributs ( i.j ), les abonnements ( i[j] ) et le découpage en tranches ( i[:j] ). Contrairement aux noms, qui se réfèrent directement aux objets, les trois font indirectement référence à des éléments de objets.

L'exemple de code inclut les deux noms ( i ) et un abonnement ( i[0] ).

i = [1,2,3]  # name `i` refers to object `[1, 2, 3]`
j = i        # name `j` refers to referent of `i`
i[0] = 5     # ???

Un CPython list utilise un tableau C de PyObject* sous le capot. Cela peut à nouveau être traduit grossièrement en code C avec des noms et des objets séparés.

typedef struct{
    int *elements[3];
} list;  // length 3 `list` type

int one = 1, two = 2, three = 3, five = 5;
list values = {&one, &two, &three};  // objects
list *i, *j;                         // names
i = &values;             // name `i` refers to object `[1, 2, 3]`
j = i;                   // name `j` refers to referent of `i`
i->elements[0] = &five;  // leading element of `i` refers to object `5`

L'important, c'est que nous n'avons changé aucun nom ! Nous avons changé i->elements[0] l'élément d'un objet que nos deux noms désignent.

Les valeurs des objets composés existants peuvent être modifiées.

Lors de la modification de la valeur d'un objet par le biais de un nom, les noms ne sont pas modifiés. Les deux sites i y j font toujours référence au même objet, dont nous pouvons modifier la valeur.

+- name i -+ -\
               \
                --> + <values> -+
               /    |  elements | --> [1, 2, 3]
+- name j -+ -/     +-----------+

L'objet intermédiaire se comporte comme suit : est similaire à un pointeur en ce sens que nous pouvons modifier directement ce vers quoi il pointe et le référencer à partir de plusieurs noms.

8voto

Keith Points 13800

Ce ne sont pas tout à fait des pointeurs, mais des références à des objets. Les objets peuvent être soit mutables, soit immuables. Un objet immuable est copié lorsqu'il est modifié. Un objet mutable est modifié in-place. Un nombre entier est un objet immuable, que vous référencez par vos variables i et j. Une liste est un objet mutable.

Dans votre premier exemple

i = 5
# The label i now references 5

j = i
# The label j now references what i references

j = 3
# The label j now references 3

print i
# i still references 5

Dans votre deuxième exemple :

i = [1, 2, 3]
# 'i' references a list object (a mutable object)
j = i
# 'j' now references the same object as 'i' (they reference the same mutable object)

i[0] = 5
# Sets first element of references object to 5

print j
# Prints the list object that 'j' references. It's the same one as 'i'.

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