web-dev-qa-db-fra.com

les variables python sont des pointeurs?

Les variables en Python ne sont que des pointeurs, à ma connaissance. 

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

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

serait 3. Mais j'ai eu un résultat inattendu pour moi, c'était 5.

De plus, mon livre Python couvre 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?

53
Lior

Nous les appelons des références. Ils travaillent comme ça

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 petites entités sont internées, 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
76
John La Rooy

Les variables ne sont pas des pointeurs. Lorsque vous affectez une variable, vous êtes liant le nom d'un objet. À partir de ce moment, vous pouvez vous référer à 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. La liaison de différentes valeurs au nom j n'a aucun effet sur i. Par conséquent, lorsque vous imprimez ultérieurement la valeur de i, la valeur est toujours 5.

Dans votre deuxième exemple, vous liez à la fois i et j à l'objet liste same. 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 et j) qui s'y réfèrent.

Documentation connexe

27
Mark Byers

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 établie dans le bloc de fonction le plus à l'intérieur contenant l'utilisation.

Quand tu fais

i = 5
j = i

c'est comme faire:

i = 5
j = 5

j ne pointe pas sur i et, après l'affectation, j ne sait pas que i existe.j est simplement lié à tout ce que i désignait au moment de l'affectation.

Si vous faites les tâches sur la même ligne, cela ressemblera à ceci:

i = j = 5

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

Ainsi, faisant plus tard 

i = 3

ne change pas ce que j pointe - et vous pouvez l'échanger - j = 3 ne changera pas ce que i pointe.

Votre exemple ne déréférence pas la liste

Alors quand tu fais ça:

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

C'est la même chose que faire ceci:

i = j = [1,2,3]

alors i et j pointent tous deux vers la même liste. Ensuite, votre exemple mute la liste:

i[0] = 5

Les listes Python sont des objets modifiables. Ainsi, lorsque vous modifiez la liste d'une référence et que vous l'examinez d'une autre référence, vous obtenez le même résultat, car il s'agit de la même liste.

11
Aaron Hall

Ce ne sont pas vraiment des pointeurs, ce sont des références à des objets. Les objets peuvent être mutables ou immuables. Un objet immuable est copié lorsqu’il est modifié. Un objet mutable est modifié sur place. Un 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.
5
Keith

TLDR: Python _ Les noms fonctionnent comme des pointeurs avec de/référencement automatique mais n'autorisent pas les opérations de pointeur explicites. Les autres cibles représentent des indirections, qui se comportent comme des pointeurs.


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

L'exemple de code Python inclut les noms (i) et les 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 des noms et des objets séparés.

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`

La partie importante est que "les noms comme des pointeurs" ne stockent pas les objets! Nous n'avons pas défini *i = five, mais i = &five. Les noms et les objets existent indépendamment les uns des autres.

Les noms uniquement désignent les objets existants en mémoire.

Lors de l'attribution de nom en nom, aucun objet n'est échangé! Lorsque nous définissons j = i, cela équivaut à j = &five. Ni i ni j ne sont connectés à l'autre.

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

En conséquence, changer la cible d'un nom n'affecte pas l'autre . Il ne fait que mettre à jour le nom de ce nom spécifique.


Python a également autres types d'éléments de nom : références d'attribut (i.j), abonnements (i[j]) et découpage (i[:j]). Contrairement aux noms, qui font directement référence à des objets, les trois font référence indirectement à des éléments de objets.

L'exemple de code comprend 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* pointeurs sous le capot. Cela peut à nouveau être approximativement traduit en code C avec des noms et des objets distincts.

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`

La partie importante est que nous n'avons changé aucun nom! Nous avons changé i->elements[0], l'élément d'un objet désigné par nos noms.

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

Lorsque vous modifiez la valeur d'un objet à l'aide d'un nom , les noms ne sont pas modifiés. i et j font toujours référence au même objet, dont la valeur peut être modifiée.

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

L'objet intermédiaire se comporte comme un pointeur, en ce sens qu'il est possible de changer directement son objet et de le référencer à partir de plusieurs noms.

4
MisterMiyagi

L'assignation ne modifie pas les objets; tout ce qu'il fait est de changer où la variable pointe. Changer où des points variables ne changera pas où un autre point.

Vous pensez probablement au fait que les tableaux et les dictionnaires sont des types mutables. Il existe des opérateurs pour modifier les objets réels sur place, et si vous utilisez l'un de ceux-ci, vous verrez le changement dans toutes les variables pointant sur le même objet:

x = []
y = x
x.append(1)
# x and y both are now [1]

Mais l’assignation ne fait que déplacer le pointeur:

x = [2]
# x is now [2], y is still [1]

Les nombres sont des types de valeur, ce qui signifie que les valeurs réelles sont immuables. Si vous faites x=3; x += 2, vous ne transformez pas le nombre 3 en le nombre 5; vous faites juste que x pointe sur 5 au lieu de 3. Le 3 reste inchangé et toutes les variables pointant dessus verront toujours 3 comme valeur.

(Dans l'implémentation réelle, les nombres ne sont probablement pas des types de référence et les variables contiennent en fait une représentation de la valeur directement plutôt que de la désigner, mais cette distinction ne modifie pas la sémantique des types de valeur.)

1
Mark Reed

quelle que soit la variable est sur le côté gauche du signe '=' est assignée avec la valeur sur le côté droit de '='

i = 5

j = i --- j a 5 

j = 3 --- j a 3 (écrase la valeur de 5) mais rien n'a été changé concernant i

print(i)-- donc ceci en imprime 5

Lorsque vous définissez j=3, le libellé j ne s'applique plus (points) à i, il commence à pointer sur l'entier 3. Le nom i fait toujours référence à la valeur que vous avez définie à l'origine, 5.

1
mbatchkarov

En Python, tout est objet, y compris les morceaux de mémoire eux-mêmes qui vous sont retournés. Cela signifie que, lorsqu'un nouveau bloc de mémoire est créé (quel que soit ce que vous avez créé: int, str, objet personnalisé, etc.), vous avez un nouvel objet de mémoire. Dans votre cas, c’est l’affectation à 3 qui crée un nouvel objet (mémoire) et a donc une nouvelle adresse. 

Si vous exécutez ce qui suit, vous voyez ce que je veux dire facilement.

i = 5
j = i
print("id of j: {}", id(j))
j = 3
print("id of j: {}", id(j))

OMI, au niveau de la mémoire, c’est la compréhension/différence essentielle entre C et Python. En C/C++, vous recevez un pointeur de mémoire (si vous utilisez bien sûr la syntaxe du pointeur) au lieu d'un objet de mémoire, ce qui vous donne plus de flexibilité pour modifier l'adresse référencée.

0
stdout