Dans cette leçon, nous donnons plus de détails sur la façon dont les listes sont stockées Python. Copier et comparer les listes peuvent avoir des conséquences inattendues. Nous allons expliquer comment Python fonctionne en interne à ce niveau, afin de comprendre et éviter les erreurs. Le point clé est que plusieurs variables peuvent "pointer" ou "partager" la même liste. Vers la fin de la leçon, nous décrivons l'opérateur "is
" qui indique si deux variables pointent vraiment exactement vers la même liste.
Exemple
Disons que nous voulons créer un fragment de code pour convertir une liste de longueurs exprimées en pouces appelée tailleOriginale
, en une liste de même longueur, mais en centimètres appelée tailleConvertie
. La façon la plus naturelle de faire est d'utiliser la ligne tailleConvertie = tailleOriginale
pour faire une copie, et ensuite passer en revue et modifier toutes les valeurs:
tailleOriginale = ["lettre", 8.5, 11] # format de papier en pouces tailleConvertie = tailleOriginale # faire une copie tailleConvertie[1] = tailleConvertie[1] * 2.54 # convertir en cm tailleConvertie[2] = tailleConvertie[2] * 2.54 # convertir en cmMaintenant, nous allons voir si cela fonctionne réellement. Si vous imprimez
tailleConvertie
alors il donne ["lettre", 21,59, 27,94]
comme prévu. Mais il y a une grosse surprise si vous aussi imprimer tailleOriginale
: les valeurs de tailleOriginale
ont changé! Nous illustrons celà ci-dessous:
Nous allons maintenant donner une explication détaillée de ce qui s'est passé, en utilisant des diagrammes. Le principal problème est que tailleConvertie = tailleOriginale
n'a pas vraiment copié toute la liste: il a simplement copié une référence (flèche) à cette liste.
Cliquez sur les titres des diapositives de changer les onglets.
ville = "Moose Factory" population = 2458 employeur = villece tableau (avec une bordure noire) montre que la mémoire Python ressemble à ceci:
Le nom de la première variable est la
ville
et sa valeur est la chaîne "Moose Factory"
. Le nom de la deuxième variable est la population
et sa valeur est l'entier 2458
. Le nom de la troisième variable est l'employeur
et sa valeur est la chaîne "Moose Factory"
, la valeur de ville
.maListe = ["Moose Factory", 2458]Cela va juste créer une variable, nommée
maListe
. Une liste est créée, et la valeur de maListe
est égale au "pointeur" ou "lien" à cette liste. Nous représentons la liste à l'aide d'une boîte, et les valeurs des entrées de la liste sont indiquées à l'intérieur de la boîte à côté de leurs indices correspondants. La liste est représentée en bleu.La flèche indique que
maListe
fait référence à cette nouvelle liste. L'élément à l'index 0 de la liste est la chaîne "Moose Factory"
, et l'élément à l'index 1 est le nombre entier 2458
. Par exemple, si maListe[1]
est affiché, alors Python va afficher 2458
.maListe = ["Moose Factory", 2458] maListe[1] = maListe[1] + 1Python calcule
2458 + 1
, soit 2459
, ce qui remplace la valeur à l'index 1 de la liste. Après la mise à jour, nous avons le schéma ci-dessous :(La barre 2458 ne fait pas partie de la mémoire de Python, il est seulement indiqué pour souligner le changement.)
tailleOriginale
et tailleConvertie
tailleOriginale = ["lettre", 8.5, 11]et la mémoire Python ressemble à la figure ci-dessous après que cette ligne soit exécutée. Nous avons créé une liste de longueur 3.
tailleConvertie = tailleOriginaleet nous voilà devant le point clé : à la deuxième ligne,
=
ne duplique pas la liste. Au lieu de cela,une nouvelle référence à la même liste a été copiée. Ceci est illustré par deux flèches différentes pointant vers la même boîte de. C'est tout à fait différent de copier les numéros ou les chaînes (comme dans la première diapositive). Comme le montre le schéma, nous avons deux variables, qui font toutes deux référence à la même liste.
tailleConvertie[1] = tailleOriginale[1]*2.54Python considère
tailleConvertie
, regarde la valeur d'indice 1 (8.5
) et le multiplie par 2.54
, puis remplace la valeur, comme illustré. Toutefois, étant donné que tailleOriginale
référence la même liste, un effet secondaire est que nous avons perturbé tailleOriginale
!(Encore une fois 8.5 est représenté ici pour souligner le changement.)
tailleConvertie[2] = tailleConvertie[2]*2.54qui affecte l'autre valeur de la liste. Après l'éxecution de cette ligne, la mémoire Python ressemble au schéma ci-dessous.
Maintenant quand nous imprimons soit
tailleConvertie
soit tailleOriginale
, Python affiche ["lettre", 21.59, 27.94]
.Une autre façon de regarder cet exemple est de l'exécuter à travers l'outil de visualisation. Si vous faites cela, au lieu de flèches, vous remarquerez que les listes sont nommées par leur "ID". Chaque liste existe en fait à une «adresse» égale à son identité; une valeur d'adresse est comme une flèche pointant vers la boîte à cette adresse ID. |
Comment copier correctement une liste en Python
Bien qu'il soit parfois utile d'avoir de multiples références à la même liste, ici ce n'est pas ce que nous voulions.
Nous allons vous donner trois solutions. Elles sont toutes à peu près équivalentes, mais chacune d'elle va nous apprendre un fait nouveau sur Python, donc les trois méritent d'être lues.
Méthode 1, en utilisant [:]
Ci-dessus, nous démontrons que listeCopiee = listeOriginale[:]
crée une vraie copie de l'ancienne liste. Bien que cette syntaxe semble étrange, elle est en relation avec quelque chose que nous avons déjà vu. Dans la leçon sur les chaînes, nous avons introduit une façon d'extraire une sous-chaîne: chaine[premier:arriere]
renvoie la sous-chaîne commençant par l'indice premier
et se terminant avec un indice arriere-1
. Nous avons mentionné que cela peut également s'appliquer à la création de sous-listes de listes. En outre,
- si vous omettez
premier
, alors il prend la valeur par défaut0
; - si vous omettez
arriere
, alors il prend la valeur par défautlen
(la longueur de la liste ou chaîne).
Alors ce qui se passe réellement, c'est que listOriginale[:]
est la création d'une sous-liste, mais contenant toutes les données de la liste originale, c'est donc une nouvelle copie.
Méthode 2, en utilisant copy.copy()
Il existe un module appelé copy
, qui contient plusieurs méthodes liées au copiage. Le plus simple est copy.copy(L)
, qui lorsque vous appelez cela sur une liste L
, retourne une copie réelle de L
.
Le copy.copy()
fonctionne aussi pour d'autres types d'objets, mais nous n'en discuterons pas ici.
Méthode 3, en utilisant list()
Le dernier moyen de faire une vraie copie utilise la fonction list()
. Ordinairement, list()
est utilisé pour convertir les données d'autres types en listes. (Par exemple list("salut")
convertit la chaîne "salut" en une liste de 5 éléments, chaque element étant un character.) Mais si vous tentez de convertir quelque chose qui est déjà une liste en une liste, cela donne tout simplement une copie.
Les listes comme arguments
Notez qu'en raison de la façon dont les listes sont manipulées, une fonction qui accepte une liste d'argument peut réellement changer le contenu de la liste. (Vous avez vu ce déjà dans l'exercice remplace
.)
def fonc(liste): liste[0] = liste[0] + liste[1] liste[1] = liste[1] + liste[0] data = [3, 4] fonc(data) print(data[0]*data[1])
fonc
, nous changeons la valeur à l'indice 0 à 7, et la valeur à l'indice 1 à 7+4=11. Alors nous avons la sortie 7*11.Comparaison de listes en utilisant is
Quand deux variables de liste L1
et L2
sont elle égales? Il a deux façons d'interpréter cette question :
- Même identité: Est-ce que
L1
etL2
pointent ou se réfèrent au même objet mémoire de type liste? - Mêmes valeurs: Est-ce que le contenu de la liste
L1
est égal au contenu de la listeL2
?
Il se trouve que, en Python, l'opérateur d'égalité standard ==
a le sens de Mêmes valeurs, comme le montre l'exemple ci dessous.
Pour tester le cas de Même identité, Python a l'opérateur is
. Il s'utilise la même manière que ==
: la syntaxe
«list1» is «list2»
renvoie True
si les listes pointent vers la même liste, et False
si elle pointent vers des listes différentes (même si elles ont un contenu identique).
True
s'affiche dans ce programme? (Faites un schéma pour suivre la trace du calcul)
liste1 = [9, 1, 1] liste3 = list(liste1) liste2 = liste1 liste4 = liste3[:] liste1[0] = 4 liste5 = liste1 print(liste1 is liste2, liste2 is liste3, liste3 is liste4, liste4 is liste5) print(liste1 == liste2, liste2 == liste3, liste3 == liste4, liste4 == liste5)
True False False False
True False True False Il ne faut pas utiliser is avec des chaînes ou des nombres car == teste déjà l'égalité, et le comportement de is est difficile à prédire avec des chaînes ou des nombres. |
Listes imbriquées
Nous avons déjà vu la plupart des informations importantes, mais il y a une autre situation usuelle qui est digne d'attention. Une liste imbriquée est une liste dont les valeurs sont elle-mêmes une liste, par exemple
exemple = [365.25, ["premier", 5]]
montre une liste imbriquée. La liste externe, vers laquelle pointe sample
, a deux éléments; l'élément à l'index 0 est une nombre flottant et l'élément l'index 1 est une liste interne. Cette liste interne est ["premier", 5]
. (Il peut y avoir plus de niveaux d'imbrication, bien entendu.) Lors de l'utilisation des listes imbriquées, gardez à l'esprit que :
- L'application des trois méthodes ci-dessus à
exemple
ne copierar que la liste externe, mais non la liste interne. Ainsicopy(exemple)[1] is exemple[1]
, signifie que la copie a toujours une référence vers une partie de la liste originale. Ce n'est probablement pas ce que vous souhaitez. C'est la méthodecopy.deepcopy()
qui effectue la copie à tous les niveaux d'imbrication. - Tester des listes avec l'opérateur
==
est intuitif: Python applique récursivement==
sur chaque élément de la liste. Par exemple[[1, 2], 3]==[[1, 2], 3]
a la valeurTrue
, et[[1, 2], 3]==[1, 2, 3]
a la valeurFalse
puisque les premiers éléments sont différents ([1, 2] != 1
).
Vous avez maintenant terminé cette leçon! Le matériel supplémentaire suivant est facultatif.
Tuples: ("listes", "immuables")
Nous avons mentionné ci-dessus que lorsque vous appelez une fonction sur une liste, il peut modifier la liste. Parfois, vous voulez rendre cela impossible! Une solution en Python est de créer des tuples, qui sont les mêmes objets que les listes, sauf qu'ils ne peuvent jamais être changé. Nous disons que les listes sont mutables et les tuples sont immuables. (Les chaînes et les nombres sont également immuables.) Cela peut être un moyen utile pour éviter toute erreur de programmation, de se prémunir contre la modification intenpestive de listes. Les tuples sont presque identiques aux listes, sauf qu'ils utilisent des parenthèses rondes ()
au lieu de crochets []
. Vous pouvez convertir des tuples et des listes à l'aide de la construction tuple()
et list()
.
Vers ∞
et au-delà: Auto-confinement
Il est possible d'avoir une liste qui se contient elle-même ! Il suffit de faire une liste, puis rediriger un de ses éléments pour qu'il pointe vers la liste elle-même :
Notez que la sortie du moteur Python est assez intelligent pour reconnaître que la liste reboucle sur elle-même: il affiche "...
" au lieu de l'impression detout L
, pour éviter une boucle infinie.