Apprenez en lisant dans l'ordre

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.

Arguments immuables
appelantx = 5dans la fonctionx = 5x = 5(inchangé)x = 15(nouvelle valeur)passerx += 10
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

Écris une fonction de conversion TVA et confirme que modifier l'argument int n'affecte pas l'extérieur.

① Définis def add_tax(price):, fais price = int(price * 1.1), puis affiche f"inside: {price}". (Pas de valeur de retour cette fois.)

② Pose base_price = 1000, appelle add_tax(base_price), puis affiche f"outside: {base_price}".

La réussite, c'est que le base_price extérieur reste inchangé.

(L'explication apparaît une fois que ça tourne correctement.)

Éditeur Python

Exécuter le code pour voir le résultat

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.

Arguments mutables
appelantmy_list = [1, 2, 3]dans la fonctionitems = [1, 2, 3]my_list =[1, 2, 3, 100](change aussi)items =[1, 2, 3, 100]passer la même boîteitems.append(100)répercuté
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.

Ressens directement comment les données extérieures se font modifier sans le vouloir.

① Définis def add_item(cart, item):, exécute cart.append(item), puis affiche f"inside: {cart}". (Pas de return.)

② Prépare my_cart = ["lait", "pain"], appelle add_item(my_cart, "œufs"), puis affiche f"outside: {my_cart}".

Confirme que "œufs" finit aussi dans le my_cart extérieur (c'est la différence avec le cas immuable de la section précédente).

Éditeur Python

Exécuter le code pour voir le résultat

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.

Crée une nouvelle boîte avec .copy() avant de modifier
items = cart.copy()items.append(x)le cart originalreste pareilaucun effet
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

Réécris la fonction de la section précédente en une version sûre qui ne modifie pas l'appelant.

① Définis def add_item_safely(cart, item):, copie l'entrée avec items = cart.copy(), exécute items.append(item), et termine par return items.

② Prépare my_cart = ["lait", "pain"] et capture le résultat avec new_cart = add_item_safely(my_cart, "œufs").

print() à la fois my_cart et new_cart et confirme que le my_cart original est inchangé alors que seul new_cart a "œufs" ajouté.

Éditeur Python

Exécuter le code pour voir le résultat
QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

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)

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 ?