Aller au contenu

Les listes sont muables

Modifier un élément

Contrairement aux chaînes de caractères et aux tuples, les listes sont muables. Cela signifie que l'on peut changer un élément de la liste sans changer d'objet liste.

On peut voir l'objet conteneur (liste, tuple, chaîne) comme une bouteille:

  • Si la bouteille est une chaîne ou un tuple, on voit ce qu'il y a dans la bouteille mais elle est bien scellée et il est impossible d'en modifier son contenu. Pour avoir un autre contenu, on doit remplir une autre bouteille.
  • Si la bouteille est une liste, on peut voir ce qu'il y a dans la bouteille et la bouteille reste ouverte: on peut modifier une partie de son contenu, ou ajouter du contenu ou encore enlever du contenu.

Important

En python:

  • les objets de type str, de type tuple, de type int, de type float sont des immuables.
  • Les objets de type list sont muables.

Pour constater que le conteneur de la liste n'est pas modifié même lorsque le contenu est modifié, on peut utiliser la fonction id qui donne un identifiant d'un objet python. On peut considérer que cet id est l'adresse en mémoire de l'objet. On constate dans le code ci-dessous que l'objet liste reste le même après avoir modifié l'un de ses éléments (seuls les objets définissant son contenu ont changé):

>>> tab = [3, 4, 5, 6]
>>> id(tab)
139779891177608
>>> tab[1] = 42
>>> tab
[3, 42, 5, 6]
>>> id(tab)
139779891177608

Schéma après la ligne

tab = [3, 4, 5, 6]

Schéma après la ligne

tab[1] = 42

L'objet tab est le même, mais son contenu a été modifié: l'objet ciblé par tab[1] a changé.

Plusieurs étiquettes sur une liste

Quel sera le contenu de a, de b, de c en dernière ligne? Expliquer.

>>> a = [2, 3, 4, 5, 6]
>>> b = a
>>> c = [2, 3, 4, 5, 6]
>>> b[-1] = 666
>>> a[0] = 42
Réponse
>>> a = [2, 3, 4, 5, 6]
>>> b = a
>>> c = [2, 3, 4, 5, 6]
>>> b[-1] = 666
>>> a[0] = 42
>>> a
[42, 3, 4, 5, 666]
>>> b
[42, 3, 4, 5, 666]
>>> c
[2, 3, 4, 5, 6]
  • Après les lignes:
>>> a = [2, 3, 4, 5, 6]
>>> b = a
>>> c = [2, 3, 4, 5, 6]

on dispose de deux objets de type list distincts. L'un porte deux étiquettes (a et b), l'autre porte une étiquette (c).

  • Après la ligne
>>> b[-1] = 666

on est dans la situation suivante:

  • Après la ligne
>>> a[0] = 42

on est dans la situation suivante:

Pour vérifier que a et b désigne la même bouteille, on peut utiliser is:

>>> a is b # pour vérifier que a et b sont deux étiquettes du même objet
True
>>> a == b # pour vérifier que a et b ont les mêmes contenus
True

Pour vérifier que deux listes ont même contenu mais sont des listes (conteneurs) différentes:

>>> d = [2, 3]
>>> e = [2, 3]
>>> d == e # True car les listes d et e ont même contenu
True
>>> d is e # False car d et e sont des étiquettes  ciblant des objets différents
False

Note

Sur la doc python.

Ajouter un élément

Les chaînes et les tuples étant immuables, on ne peut pas modifier l'un de leurs éléments, ni en ajouter un sans créer un nouveau conteneur.

Par contre, on peut ajouter un élément à une liste sans changer la liste (on continue de remplir la bouteille sans changer de bouteille).

Pour ajouter un élément à une liste (en fin de liste), on utilise la méthode append:

>>> a = [2, 3, 4]
>>> id(a)
139779891299912
>>> a.append(42)
>>> a
[2, 3, 4, 42]
>>> id(a)
139779891299912
  • Après la première ligne
>>> a = [2, 3, 4]

on a la situation suivante:

  • Après la ligne
a.append(42)

l'objet désigné par a est le même objet mais avec un élément de plus:

Exercice

Écrire un code possible pour le corps de la fonction suivante:

def moyenner(tab):
    """
    tab -- liste de flottants

    remplace chaque élément de tab par la moyenne des éléments de tab
    (la fonction ne renvoie rien, 
    elle agit en place sur la liste passée en argument)
    """
Un code
def moyenner(tab):
    """
    tab -- liste de flottants

    remplace chaque élément de tab par la moyenne des éléments de tab
    """
    somme = 0
    for element in tab: somme += element
    moyenne = somme/len(tab)
    for indice in range(len(tab)): tab[indice] = moyenne

Exemple d'utilisation: fichier ipynb (version html)

Détails

Détaillons un peu ce qu'il se passe sur les variables avec l'essai suivant.

def moyenner(tab):
    """
    tab -- liste de flottants

    remplace chaque élément de tab par la moyenne des éléments de tab
    """
    somme = 0
    for element in tab: somme += element
    moyenne = somme/len(tab)
    for indice in range(len(tab)): tab[indice] = moyenne


if __name__ == '__main__':
    L = [3, 4, 2, 1]
    moyenner(L)
    print(L)

Après la ligne:

L = [3, 4, 2, 1]

on a la situation:

On appelle alors la fonction moyenner:

moyenner(L)

On a alors la situation suivante:

On exécute les lignes suivantes du corps de la fonction moyenner:

somme = 0
for element in tab: somme += element
moyenne = somme/len(tab)

On a la situation suivante:

Puis on exécute la dernière ligne du code de la fonction moyenner:

for indice in range(len(tab)): tab[indice] = moyenne

La situation devient:

On sort de la fonction: tout nom local à la fonction disparaît.

La situation:

Remarque. On a laissé la représentation d' objets créés lors de l'exécution de la fonction. Ceux qui n'ont plus d'étiquette peuvent être considérés comme ayant disparus: on n'a plus accès à ces objets puisqu'ils n'ont plus d'étiquette.

Exercice

Nous avons vu comment faire une copie d'une liste par compréhension:

def copier(tab):
    """
    tab -- liste

    renvoie une liste de même contenu que tab
    >>> A = [42, 666, 1789]
    >>> B = copie(A)
    >>> B
    [42, 666, 1789]
    """
    return [element for element in tab]

Proposer un équivalent de cette fonction utilisant la méthode append.

Un code
def copier(tab):
    """
    tab -- liste

    renvoie une liste de même contenu que tab
    >>> A = [42, 666, 1789]
    >>> B = copie(A)
    >>> B
    [42, 666, 1789]
    """
    nvTab = []
    for element in tab:
        nvTab.append(element)
    return nvTab
Schémas

Voyons un peu le détail avec l'appel suivant:

def copier(tab):
    """
    tab -- liste

    renvoie une liste de même contenu que tab
    >>> A = [42, 666, 1789]
    >>> B = copie(A)
    >>> B
    [42, 666, 1789]
    """
    nvTab = []
    for element in tab:
        nvTab.append(element)
    return nvTab



if __name__ == '__main__':
    L = [3, 4, 2, 1]
    T = copier(L)
    print(L)
    print(T)

Après la ligne:

L = [3, 4, 2, 1]

on a la situation:

On appelle alors la fonction copier (partie droite de l'instruction d'affectation en ligne 20):

copier(L)

La situation:

Puis on exécute la ligne

nvTab = []

Puis on exécute les lignes

for element in tab:
        nvTab.append(element)

Enfin la fonction renvoie cette liste et on l'affecte à T: