Question 1Après l'exécution de ce code, quelle est la valeur de x ?def f(value):
value += 10
x = 5
f(x)
print(x)
Arguments de fonction et références d'objets — les mutables se modifient à l'intérieur des fonctions
Apprends comment les arguments de fonction Python se comportent selon leur type pour éviter les bugs liés aux mutables.
Quand tu appelles une fonction, le comportement change selon que l'argument est un type mutable (list / dict / set) ou un type immuable (int / str / tuple). Cet article parcourt cette différence et les motifs de base pour écrire des fonctions sûres.
Arguments immuables — les changements à l'intérieur ne fuient pas dehors
Les entiers, les flottants, les chaînes et les tuples sont immuables (impossibles à modifier sur place). Reçois-en un comme argument et réécris-le avec += 10 ou = some_other_value, et la variable de l'appelant n'est pas touchée.
Sous le capot, += 10 crée une nouvelle valeur et ne réaffecte que le nom d'argument interne — le nom de l'appelant pointe toujours sur l'original.
def try_modify_number(value):
value += 10
print(f"inside: {value}")
x = 5
try_modify_number(x)
print(f"outside: {x}")
# inside: 15
# outside: 5 <- toujours l'original
# Même histoire pour les chaînes et les tuples
def try_modify_text(text):
text = text + " world"
print(f"inside: {text}")
message = "hello"
try_modify_text(message)
print(f"outside: {message}") # hello
Arguments mutables — les changements à l'intérieur fuient dehors
À l'inverse, quand tu passes un type mutable comme list / dict / set et que tu appelles quelque chose comme .append() ou .update() pour modifier le contenu sur place, la variable de l'appelant voit le même changement.
C'est parce qu'au moment où l'argument est passé, le nom de l'appelant et le nom d'argument interne partagent la même boîte. C'est le même mécanisme que y = x vu dans l'article précédent sur mutable vs immuable — qui se produit maintenant à la frontière de l'appel.
def try_modify_list(items):
items.append(100)
print(f"inside: {items}")
my_list = [1, 2, 3]
try_modify_list(my_list)
print(f"outside: {my_list}")
# inside: [1, 2, 3, 100]
# outside: [1, 2, 3, 100] <- l'extérieur a changé aussi
# Pareil avec les dicts
def set_role(user, role):
user["role"] = role
admin = {"name": "Alice"}
set_role(admin, "admin")
print(admin) # {'name': 'Alice', 'role': 'admin'}
Quand les mutations internes fuient dehors, la cause est dure à pister
cart.append(...) a l'air parfaitement intentionnel sur sa propre ligne. Mais comme l'append à l'intérieur de la fonction touche aussi le my_list extérieur, tu peux te retrouver avec des symptômes du genre « la liste a grossi en silence » qui apparaissent dans un endroit complètement sans rapport.
Modifier seulement à l'intérieur de la fonction — protéger les arguments avec .copy()
Quand tu veux laisser les données de l'appelant tranquilles et seulement changer des choses à l'intérieur de la fonction, mets simplement .copy() au début de la fonction. Copie la liste, le dict ou le set entrant dans une boîte séparée avant de la modifier, et l'appelant n'est pas affecté.
Renvoie la version modifiée avec return, et l'appelant peut tenir à la fois le panier original et le nouveau panier. Une fois ce motif dans les doigts, tu écriras des fonctions sûres et sans effets de bord (pas de modification de l'appelant).
Le flux complet : ① copier l'argument avec .copy() dans une boîte séparée → ② éditer la copie → ③ retourner le résultat. Mémorise ces trois étapes et tu pourras concevoir en toute sécurité n'importe quelle fonction qui prend des arguments mutables.
def add_item_safely(cart, item):
items = cart.copy() # copie dans une boîte séparée avant d'éditer
items.append(item)
return items # renvoie le résultat via return
my_cart = ["lait", "pain"]
new_cart = add_item_safely(my_cart, "œufs")
print(my_cart) # ['lait', 'pain'] <- pas touché
print(new_cart) # ['lait', 'pain', 'œufs'] <- une liste séparée
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Après l'exécution de ce code, quelle est la valeur de my_list ?def g(items):
items.append(100)
my_list = [1, 2, 3]
g(my_list)
print(my_list)
Question 3Quand tu veux renvoyer une nouvelle liste sans modifier la liste de l'appelant, quelle est la première chose à faire ?