Try   HackMD

Types mutables, types persistants : quelles différences ? Quelles conséquences

Dans ce TP nous approfondissons les notions de types mutables et persistants.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
Prérequis: les opérations de base sur les types list et tuple doivent être maîtrisées : ajout, suppression, initialisation, affectation, copie, etc.

Différence entre type mutable et type persistant

Les types dits mutable en anglais (muable en français) sont modifiables. Ainsi, une liste, après sa déclaration peut subir un certain nombre de modifications et être mise à jour au fur et à mesure de l'évolution du programme dans lequel elle est utilisée.

Exemple :

maListe = [1,2,3,4] maListe.append(5) print(maListe) maListe.pop() print(maListe)

Les tuples (p-uplet) de leur côté ne peuvent être modifiés. Ils sont dits immuables ou persistants en français (immutable en anglais).
1) Relevez le message d'erreur obtenu après exécution de ce script

monUplet = (1,2,3,4,5) monUplet[0] = 8 monUplet.append(67) monUplet.pop()

TypeError: 'tuple' object does not support item assignment. Ce qui signifie que le tuple ne peut changer de valeur, il ne peut être modifier, il n'est pas mutable.

2) À votre avis, est-il pertinent d'utiliser des p-uplets pour stocker des notes d'élèves ?

mon_P_Uplet = (1,2,3,4,5)
mon_P_Uplet[4] = 4
mon_P_Uplet.append(12)
print(mon_P_Uplet)

Pour stocker des notes d'éléves il faut pouvoir les changer ou bien les modifier voire même en ajouter.

La persistance ou la muabilité d'un type de données en Python possède plusieurs conséquences que nous allons explorer dans ce TP

Conséquence 1 : l'utilisation des alias

Pour les listes

La création d'une liste contenant les valeurs ["a", "b", "c"] se fait en allouant un espace mémoire dans le tas.
Le script suivant génère l'allocation de deux espaces mémoire distincts bien que les deux variables possèdent les mêmes valeurs.

liste1= ["a", "b", "c"] liste2 = ["a", "b", "c"]

3) Si l'on modifie liste1, cela aura t-il des conséquences sur liste2 ?

liste1= ["a", "b", "c"]
liste2 = ["a", "b", "c"]
liste1.append("d")
print(liste1)
print(liste2)

Intéressons-nous maintenant au script suivant :

liste3= ["a", "b", "c"] liste4 = liste3

4) Modifier liste3 à l'aide d'une opération de votre choix.

liste3= ["a", "b", "c"] liste4 = liste3 liste3.append("d") print(liste3) print(liste4)

5) Affichez la valeur de liste3 et de liste4. Que constatez-vous ?

liste3= ["a", "b", "c"] liste4 = liste3 liste3.append("d") print(liste3) print(liste4)

Nous constatons donc que lorsque l'on change la liste 3, la liste 4 change aussi. Cela est du au fait que la liste 4 a pour valeur la liste 3 (ligne 2)
6) Quelle est la différence entre la copie (liste5) et la création d'alias(liste4)

liste5 = liste3.copy()
liste1= ["a", "b", "c"] liste2=["e","f","g"] liste3=liste1 #alias liste4 = liste1.copy() #copie liste1[1] = 'z' print(liste1) print(liste2) print(liste3) print(liste4)

Nous remarquons que l'utilisation de copy permet de copier la première version de la liste (avant changements) tandis que alias recopie la dernière version de la liste.
7) À partir des observations effectuées en questions 5 et 6, donnez une définition à la notion d'alias.
Alias est donc une affectation d'une liste à une autre en prenant en compte les changements effectués.

Pour les tuples

Vérifions maintenant
8)Testez le code suivant. Que constatez-vous ?

tuple1 = (1,2,3) tuple2 = tuple1 tuple1 = tuple1 + (4,5) print(tuple1) print(tuple2)

Elle affiche : (1, 2, 3, 4, 5) et (1, 2, 3) nous remarquons donc que pour les tuples l'affectation ne prend pas en compte les changements contrairement aux listes.

L'instruction tuple1 = tuple1 + (4,5) est exécutée sans générer d'erreurs car elle ne modifie pas la valeur originelle de tuple1. Elle ne fait qu'affecter (à l'aide de l'opérateur d'affectation =) une nouvelle valeur à tuple1 sans toucher à l'ancienne.

9)Quelles conclusions pouvez-vous en tirer ?
Les affectations chez les listes et les tuples ne fonctionnent pas de la même façon.

Conséquence 2 : l'utilisation de la copie

10) Créer une fonction copie_liste qui prend en paramètre une liste et retourne sa copie
Exemple :

liste1 = [1,2,3] liste2 = copie_liste(liste1)
def copieListe(liste1): liste2 = liste1.copy() return liste2 print(copieListe([1,2,3,4]))

11) Testons l'utilisation de la fonction précédente sur une matrice

liste3 = [[1,2],[3,4]] liste4 = copie_liste(liste3) liste3[0][1] = 6

12) Afficher liste3 et liste4. Quelles sont les modifications observées ? Donnez une explication suite à ces observations.
Renvoie : [[1, 6], [3, 4]] et [[1, 6], [3, 4]]
Sur une matrice, les modifications faites sur une première liste sont également appliquéessur ue autre liste avec la fonction 'copy'.

Et maintenant, un peu de récursivité :)

13) Créer une fonction récursive qui permet d'effectuer la copie d'une liste qu'il s'aggisse d'une liste simple, d'une matrice ou d'une liste de listes de listes

def copie_liste_recursive(liste): ... return copie
def copie_liste_recursive(t): if isinstance(t, list): res=[] for x in t: res.append( copie_liste_recursive(x)) return res else: return t l1 = [[9,0],[6,7]] l2 = [1,2,3,4,5] l3 = [[[9,0],[6,7]], [[1,2,3,4,5],[1,2]]] print(copie_liste_recursive(l1)) print(copie_liste_recursive(l2)) print(copie_liste_recursive(l3))

14) Tester votre fonction récursive à l'aide des variables suivantes :

l1 = [[9,0],[6,7]] l2 = [1,2,3,4,5] l3 = [[[9,0],[6,7]], [[1,2,3,4,5],[1,2]]]

renvoie :
[[9, 0], [6, 7]]
[1, 2, 3, 4, 5]
[[[9, 0], [6, 7]], [[1, 2, 3, 4, 5], [1, 2]]]

Conséquence 3 : les paramètres de fonction mutables

Gestion des impuretés

La création d'alias peut émerger dans différentes situations. Ainsi, lorsque l'on effectue des appels de fonctions à l'aide de variables globales (entre autres) des alias peuvent être générés.

14) Testez le code suivant et comparez les valeurs des deux variables avant et après l'appel de fonction

def fct_test(maListe): maListe.append(1) return maListe = [1,2,3] ex2 = [5] fct_test(ex2) print(maListe) print(ex2)

renvoie :
[1, 2, 3]
[5, 1]
maListe ne change pas car l'appel de fonction n'entre en paramètre que la liste ex2 donc 1 lui est rajouté.
15)En ce qui concerne le paramètre maListe de fct_test et la variable maListe, subissent-ils les mêmes modifications ? Donnez une explication.
Le paramètre subit des modifications apportées par la fonction tandis que la variable ne change pas si elle n'est pas prise en paramètre.