1. Mutable ou immuable ?

Mutables    : list, dict, set
Immuables   : tuple, str, frozenset, int, float, bool, None
a = [1, 2, 3]
b = a
b.append(4)
print(a)   # [1, 2, 3, 4] !
Piège — aliasing
b = a ne copie pas la liste : b et a référencent le même objet.

2. Exercice 1 — Ensembles et opérations non destructives

invites_alice = {"Bob", "Chloé", "David"}
invites_eva = {"Chloé", "David", "Farid"}

a) Union (|). b) Intersection (&). c) Différence (-). d) Les ensembles de départ ont-ils changé ?

Voir la réponse
union = invites_alice | invites_eva
intersection = invites_alice & invites_eva
difference = invites_alice - invites_eva
Non : |, & et - créent un nouvel ensemble sans jamais modifier les ensembles de départ (opérations non destructives).

3. Exercice 2 — L'agence spatiale (version immuable)

def deplacer(position, dx, dy, dz):
    """Renvoie une NOUVELLE position, sans modifier position."""
    ...

vaisseau1 = (0, 0, 0)
nouvelle_position = deplacer(vaisseau1, 10, 0, 5)

a) Complète deplacer. b) vaisseau1 a-t-il changé ? c) Que se passe-t-il si tu écris vaisseau1[0] = 5 ?

Voir la réponse
def deplacer(position, dx, dy, dz):
    x, y, z = position
    return (x + dx, y + dy, z + dz)
vaisseau1[0] = 5 lève une TypeError : un tuple est immuable, aucune opération ne permet de modifier un de ses éléments en place.

4. Exercice 3 — L'agence spatiale (version mutable, le bug)

position_origine = [0, 0, 0]
flotte = [position_origine] * 3
flotte[0][0] = 100
print(flotte)

a) Prédis le résultat. b) Explique avec id(). c) Corrige avec .copy(). d) Ce bug peut-il arriver avec des tuples ?

Voir la réponse
Résultat réel : [[100,0,0],[100,0,0],[100,0,0]][position_origine] * 3 crée 3 références vers le même objet liste.
flotte = [position_origine.copy() for _ in range(3)]
Avec des tuples, ce bug est impossible : même si les références sont partagées, un tuple ne peut jamais être modifié en place. L'immuabilité élimine structurellement ce bug.

Exercices défis (bonus)

Défi 1 défi

t = (1, [2, 3]). Peux-tu faire t[0]=5 ? Et t[1].append(4) ?

Voir la réponse
t[0]=5 échoue (TypeError). t[1].append(4) fonctionne : le tuple est immuable sur ses références, pas récursivement sur le contenu de celles-ci.
Défi 2 défi

Recherche collections.namedtuple et crée un type Position(x, y, z).

Voir la réponse
from collections import namedtuple
Position = namedtuple("Position", ["x", "y", "z"])
p = Position(10, 0, 5)
print(p.x)   # plus lisible que p[0]
Défi 3 défi

Écris fusionner_flottes(flotte1, flotte2) sans jamais modifier les originales.

Voir la réponse
return flotte1 + flotte2 — la concaténation de tuples crée un nouveau tuple.