Égalité structurelle et égalité physique

Quand on affecte une valeur à une variable (par exemple x = 2013), on crée en fait un pointeur (une adresse) pour désigner cette valeur qu’il faut imaginer quelque part en mémoire.

Les variables Python sont donc des références (des adresses, des pointeurs) vers des valeurs. Pour connaître l’adresse où se trouve exactement la valeur qu’on a associée à un identificateur, on utilise la fonction intégrée id.

L’égalité structurelle de deux objets signifie l’égalité de leurs valeurs, alors que l’égalité physique signifie l’égalité des adresses de ces valeurs (bien sûr l’égalité physique implique l’égalité structurelle, mais la réciproque est fausse).

Pour tester l’égalité structurelle, on utilise l’opérateur ==, et pour l’égalité physique, on utilise l’opérateur is.

Ces distinctions ne sont pas très importantes quand on manipule des objets dits “non mutables” (comme les types de base : entiers, flottants, nombres complexes, chaînes de caractères, et également les tuples qui seront étudiés plus tard).

En revanche, la distinction entre égalité physique et égalité structurelle prend tout son sens quand on modifie les éléments d’objets “mutables” (comme les listes ou les dictionnaires).

Le mieux est de prendre des exemples, en commençant par du très simple :

Continuons sur la lancée de l’exemple précédent.

Continuons, en modifiant maintenant le contenu de la seule variable y :

On va maintenant effectuer des opérations analogues, mais sur des listes (les objets de type liste seront étudiés en détail un peu plus loin) :

On continue sur cet exemple, en redéfinissant la liste y

On va maintenant recopier à nouveau x dans y, mais modifier seulement un élément de y.

L’exemple précédent est important. Les listes Python sont des objets composites (formés d’éléments a priori disparates mais rassemblés dans une même structure). Si on modifie un élément d’une liste, Python ne modifie pas l’adresse de celle-ci (en fait, il modifie seulement l’adresse de l’élément concerné dans cette liste).

Cela explique qu’après l’instruction y = x (à l’issue de laquelle x et y pointent sur une même liste en mémoire), la modification y[0] = 999 semble répercutée sur la liste contenue dans la variable x (tout simplement car les variables x et y continuent à pointer sur la même adresse).

L’instruction y = x n’a donc pas associé à y une nouvelle copie de la liste associée à x, elle a fait pointer y et x sur une même adresse (toute modification d’un des deux éléments x[k] ou y[k] sera donc “répercutée” sur l’autre).

Si on veut créer une copie d’une liste qui soit indépendante de l’original, on procédera de la façon suivante :

Remarque : l’expression x[:] est un cas particulier de la syntaxe x[a:b] qui renvoie la liste des éléments de l’objet x situés de la position a (incluse) à la position b (exclue). Par défaut a vaut 0 (c’est le début de l’objet x) et b vaut la longueur de cet objet. Ainsi x[:] renvoie donc la totalité de l’objet x (mais cette copie est indépendante de l’original).

Attention : la notion de “copie originale”, telle qu’elle a été évoquée ci-dessus (l’instruction y = x[:]) n’est tout à fait exacte que si la liste source (ici x) ne contient que des objets “non mutables” (nombres, chaînes, tuples). En effet, après la copie, l’adresse de y est différente de celle de x, mais l’adresse de chaque y[i] reste celle de x[i].

Un exemple permettra d’y voir plus clair :

Retour à l’accueil du cours Python/Numpy

Sommaire du chapitre 2

  1. Quelques types (classes) de base
  2. Opérations entre types numériques
  3. Les opérateurs avec assignation
  4. Les fonctions mathématiques du module math
  5. Le module cmath
  6. Arithmétique des entiers
  7. Valeurs booléennes et comparaisons
  8. Égalité structurelle et égalité physique