{}const=>[]async()letfn</>var
DéveloppementPython

Pourquoi votre code Python se comporte-t-il bizarrement ? Piège des arguments par défaut mutables

Nous analysons l'une des erreurs Python les plus insidieuses, qui oblige les fonctions à « se souvenir » des appels précédents. Découvrez pourquoi une liste vide devient soudainement non vide et comment travailler correctement avec les arguments par défaut.

К

Kodik

Auteur

4 min de lecture

À quoi ressemble le problème ?

Imaginez que vous écrivez une fonction pour ajouter des tâches à une liste de tâches :

def add_task(task, todo_list=[]):
    todo_list.append(task)
    return todo_list

# Ajoutons la première tâche print (add_task ("Acheter du lait")) # ['Acheter du lait']ns une deuxième tâche print(add_task("Promener le chien")) # Nous attendons : ['Promener le chien']                             # Nous obtenons : ['Acheter du lait', 'Promener le chien']

⚠️ Que s'est-il passé ? Nous nous attendions à recevoir une nouvelle liste avec une tâche, mais la deuxième tâche a été ajoutée à la première ! Et nous n'avons enregistré la liste précédente nulle part. Magie ? Non, ce sont des arguments par défaut mutables.

🔥 100 000+ étudiants déjà avec nous

Marre de lire la théorie ?
Il est temps de coder !

Kodik — une appli où tu apprends à coder par la pratique. Mentor IA, leçons interactives, projets réels.

🤖 IA 24/7
🎓 Certificats
💰 Gratuit
🚀 Commencer
Ont rejoint aujourd'hui

Pourquoi cela se produit-il ?

Lorsque Python crée une fonction, il crée un objet par défaut une fois au moment de la définition de la fonction, et non à chaque appel. Cet objet est enregistré et réutilisé à chaque appel suivant.

Voyons ce qui se passe réellement :

def add_task(task, todo_list=[]):
    todo_list.append(task)
    print(f"List ID: {id(todo_list)}")
    return todo_list

add_task("First task")   # ID de la liste : 140234567891234add_task("Deuxième tâche") # ID de la liste : 140234567891234 (le même !) add_task("Troisième tâche") # ID de la liste : 140234567891234 (et encore !)

💡 Point clé : Chaque fois, le même objet de la liste est utilisé. Tous les appels de fonction fonctionnent avec la même liste en mémoire, de sorte que toutes les modifications s'accumulent.

Quels types de données sont concernés ?

Ce problème concerne tous les types de données mutables en Python. Cela inclut les listes, les dictionnaires, les ensembles et les objets personnalisés. Mais les nombres, les chaînes et les tuples (types immuables) fonctionnent normalement, car ils ne peuvent pas être modifiés, vous ne pouvez en créer que de nouveaux.

# Code problématique avec dictionnaire def add_user(name, users_dict={}) :
    users_dict[name] = True
    return users_dict

print(add_user("Alexey"))  # {'Alexey': True}print(add_user("Maria")) # {'Alexey': True, 'Maria': True} - les deux sont ici !# Code sécurisé avec la chaîne (immutable)def greet(name, greeting="Bonjour") :    greeting = greeting + ", " + name
    return greeting

print(greet("Ivan"))   # Bonjour, Ivanprint(greet("Olga")) # Bonjour Olga - tout fonctionne comme prévu

La bonne décision.

La solution classique à ce problème consiste à utiliser None comme valeur par défaut, puis à créer un nouvel objet à l'intérieur de la fonction :

def add_task(task, todo_list=None):
    if todo_list is None:
        todo_list = []
    todo_list.append(task)
    return todo_list

# Maintenant, tout fonctionne correctementprint(add_task("Acheter du lait"))        # ['Acheter du lait']print(add_task("Promener le chien"))   # ['Promener le chien']

✅ Règle : Chaque fois que nous ne transmettons pas la liste, une nouvelle liste vide est créée. Problème résolu !

Il en va de même pour les dictionnaires :

def create_profile(name, data=None):
    if data is None:
        data = {}
    data['name'] = name
    data['created_at'] = 'today'
    return data

profile1 = create_profile("Alexey")
profile2 = create_profile("Maria")

print(profile1)  # {'name': 'Alexey', 'created_at': 'aujourd'hui'}print(profile2)  # {'name': 'Maria', 'created_at': 'aujourd'hui'}

Quand cela peut-il être utile ?

Il est intéressant de noter que parfois ce comportement peut être utilisé spécifiquement, par exemple, pour la mise en cache des résultats :

def get_config(config_cache={}):
    if 'data' not in config_cache:
        print("Loading configuration...")
        config_cache['data'] = "important settings"
    return config_cache['data']

print(get_config())  # Chargement de la configuration... paramètres importants print (get_config ()) # paramètres importants (ne sont plus chargés)

💡 Remarque : Mais dans le code réel pour la mise en cache, il est préférable d'utiliser des outils spéciaux comme functools.lru_cache, afin que le code soit plus clair.

Comment éviter cette erreur ?

Rappelez-vous simplement la règle simple : n'utilisez jamais d'objets modifiables (listes, dictionnaires, ensembles) comme valeurs par défaut dans les arguments de fonction. Utilisez toujours None et créez l'objet souhaité à l'intérieur de la fonction.

Cette erreur est si courante que de nombreux linters (outils de vérification de code) vous avertissent spécifiquement à ce sujet. Si vous utilisez PyCharm, VSCode avec pylint ou flake8, vous verrez un avertissement lorsque vous essayez d'écrire un tel code.

Apprenez Python correctement dès la première fois !

Ce sujet et de nombreux autres sujets importants peuvent être étudiés en détail dans Codique - comprendre toutes les nuances avec des explications claires et consolider en pratique avec des tâches intéressantes. Chaque sujet est accompagné d'exemples de développement réel, et les exercices pratiques permettent d'appliquer immédiatement les connaissances acquises.

Et si vous avez des questions pendant la formation ou si vous souhaitez discuter de points complexes, nous avons déjà plus 2 000 personnes partageant les mêmes idées en mode actif canal Telegram, où ils vous aideront toujours à comprendre et à vous soutenir sur le chemin de la programmation !

🎯Arrête de reporter

Tu as aimé l'article ?
Place à la pratique !

Avec Kodik, tu ne lis pas seulement — tu codes immédiatement. Théorie + pratique = vraies compétences.

Pratique instantanée
🧠L'IA explique le code
🏆Certificat

Sans inscription • Sans carte