Question 1Si une classe C a __init__(self, name), que se passe-t-il quand tu appelles C() sans arguments ?
Le constructeur __init__ et le destructeur __del__
Un guide pratique de __init__ et __del__ en Python. Attributs requis, valeurs par défaut, et la méthode de nettoyage appelée à del ou à la fin du programme.
La dernière fois tu as vu les bases des classes et instances, self, et la forme la plus simple de __init__. Cette fois on va plus loin, en utilisant __init__ pour s'assurer que chaque instance naît dans un état valide. On couvrira les arguments requis qui échouent bruyamment quand tu les oublies, les arguments par défaut pour les champs optionnels, et le destructeur correspondant __del__.
Ce qui se passe sans __init__
Si ta classe n'initialise pas correctement les attributs, tu peux te retrouver avec des instances dépourvues de ces attributs, et au moment où tu appelles une méthode qui les utilise, tu te prends un AttributeError.
Dans le code ci-dessous, User n'a qu'une méthode set_name — pas d'initialiseur. Appeler display() avant set_name plante parce que self.name n'existe pas encore.
class User:
def set_name(self, name):
self.name = name
def display(self):
print(self.name)
user = User()
user.display() # AttributeError: pas d'attribut name
# self.name n'existe pas tant que tu n'as pas appelé user.set_name("Alice")
Utiliser __init__ pour imposer les attributs requis
__init__ est la méthode spéciale que Python appelle automatiquement à la création d'une instance. On l'appelle aussi le constructeur. Les noms entourés de doubles soulignements sont des méthodes dunder — la convention Python pour les méthodes qu'il appelle à des moments spécifiques.
En déclarant __init__(self, name, email) avec des paramètres requis, oublier de passer l'un des deux arrête le programme avec TypeError. Tu ne te retrouves jamais avec un User() incomplet, donc tout le code qui suit peut compter sur le fait que « name et email sont définitivement définis ».
User("Alice", "alice@x.com") fait que Python construit une instance vide, exécute __init__, remplit les attributs et renvoie l'instance terminée. Oublie un argument et __init__ lui-même lève TypeError.class User:
def __init__(self, name, email): # paramètres requis
print("__init__ appelé")
self.name = name
self.email = email
def display(self):
print(f"{self.name} <{self.email}>")
user = User("Alice", "alice@example.com") # définit user.name à "Alice" et user.email à "alice@example.com"
# __init__ appelé
user.display()
# Alice <alice@example.com>
# Oublier un argument lève TypeError
# User() # → TypeError: missing 2 required positional arguments
Arguments par défaut pour les champs optionnels
__init__ est une fonction normale, donc tu peux lui donner des arguments par défaut. Écris quelque chose comme age=0, et l'appelant peut soit le sauter (et obtenir la valeur par défaut), soit passer une valeur. Cela te permet de scinder ta conception entre « attributs requis » et « attributs optionnels ».
class User:
def __init__(self, name, email, age=0): # age est optionnel
self.name = name
self.email = email
self.age = age
alice = User("Alice", "alice@example.com") # age omis → 0
lea = User("Léa", "lea@example.com", 30) # age fourni
print(alice.age) # 0
print(lea.age) # 30
Ne mets pas list / dict directement dans un argument par défaut
Écrire un objet mutable comme valeur par défaut — comme def __init__(self, tags=[]) — tombe dans un piège notoire : toutes les instances finissent par partager la même liste. On couvre ça en détail dans Arguments de fonction et mutabilité. C'est le même piège dans __init__, donc le correctif standard est tags=None plus if tags is None: tags = [] à l'intérieur de la fonction.
__del__ — Le nettoyage quand une instance disparaît
La méthode dunder qui fait pendant à __init__ est __del__ (le destructeur). Tandis que __init__ s'exécute à la création d'une instance, __del__ s'exécute au moment où une instance est détruite, appelée automatiquement par Python.
Il y a deux principales façons dont la destruction se produit :
- Tu la supprimes explicitement avec del nom_de_variable
- La mémoire est récupérée quand le programme se termine (toutes les instances restantes sont détruites tour à tour)
À cause du second cas, __del__ s'exécute à la fin du programme même si tu ne le déclenches pas toi-même.
del, soit à la fin du programme.__del__ ne s'exécute pas dans le navigateur
L'exécution côté navigateur de ce site est basée sur MicroPython, qui par conception n'appelle pas __del__. Les exemples de code de cette section sont à lire pour comprendre le comportement. Exécute-les dans CPython sur ton terminal local si tu veux vraiment voir __del__ se déclencher.
class User:
def __init__(self, name):
print(f"créé {name}")
self.name = name
def __del__(self):
print(f"détruit {self.name}")
alice = User("Alice") # créé Alice
lea = User("Léa") # créé Léa
del alice # détruit Alice ← se déclenche explicitement ici
print("sur le point de quitter")
# sur le point de quitter
# détruit Léa ← se déclenche automatiquement à la fin du programme
Un motif de nettoyage courant — Se retirer d'une liste
Un usage typique de __del__ est le nettoyage qui fait pendant à la création — comme « me retirer d'une liste tenue par la classe ». Dans l'exemple ci-dessous, la classe User ajoute le nom à la variable de classe users à la création et le retire à la destruction.
class User:
users = [] # variable de classe : utilisateurs actuellement en vie
def __init__(self, name):
self.name = name
User.users.append(name) # enregistrer à la création
def __del__(self):
User.users.remove(self.name) # retirer à la destruction
alice = User("Alice")
lea = User("Léa")
print(User.users) # ['Alice', 'Léa']
del alice
print(User.users) # ['Léa']
L'utilisation directe de __del__ est rare en pratique
Le moment où __del__ s'exécute dépend du ramasse-miettes de Python, donc tu ne peux pas prédire le timing de manière fiable. Pour la libération déterministe de fichiers ou de connexions réseau, utilise plutôt les instructions with (gestionnaires de contexte) que __del__.
Cette fois on a couvert l'utilisation pratique de __init__ — imposer les attributs requis et les arguments par défaut — et rencontré le __del__ correspondant avec son motif typique. Tu peux maintenant contrôler « la durée de vie d'une instance, de la création à la destruction » depuis Python.
Ensuite, on creusera la vérité derrière self.x = ... que tu écris sans trop y penser. On démêlera les deux types de variables dans une classe — variables de classe et variables d'instance — et on suivra ce qui se crée réellement quand tu écris self.x = ..., en surveillant les références avec id().
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Quel est le moment correct où __del__ s'exécute ?
Question 3Pourquoi écrire def __init__(self, tags=[]) est-il considéré comme problématique ?