Classes/Objets Python
Programmation Orientée Objet (POO) en Python
Section titled “Programmation Orientée Objet (POO) en Python”Python est fondamentalement un langage orienté objet. La création et l’utilisation de classes et d’objets sont un élément central pour programmer efficacement en Python. Cette section présente les concepts clés du support POO de Python.
Si vous débutez en Programmation Orientée Objet (POO), la compréhension de concepts tels que les classes, les objets, l’héritage, l’encapsulation et le polymorphisme est cruciale. Pensez à consulter des documents d’introduction à la POO.
Terminologie POO Essentielle
Section titled “Terminologie POO Essentielle”- Classe : Un plan ou un modèle pour créer des objets. Elle définit les attributs (données) et les méthodes (fonctions) que posséderont les objets créés à partir de la classe.
- Objet (Instance) : Une réalisation spécifique d’une classe. Si
Dogest une classe, alors un chien spécifique commemy_dog = Dog()est un objet ou une instance de la classeDog. - Attribut : Une variable associée à une classe ou à un objet.
* Variable de classe : Partagée par toutes les instances d’une classe. Définie à l’intérieur de la classe mais en dehors de toute méthode.
* Variable d’instance : Appartient à une instance/un objet spécifique. Généralement définie à l’intérieur de la méthode
__init__en utilisantself.variable_name. - Méthode : Une fonction définie au sein d’une classe. Opère sur des objets (instances) de cette classe. Le premier paramètre d’une méthode d’instance est conventionnellement nommé
self, représentant l’instance elle-même. - Méthode
__init__(Initialiseur/Constructeur) : Une méthode spéciale appelée automatiquement lorsqu’un nouvel objet (instance) d’une classe est créé. Utilisée pour initialiser les attributs de l’objet. self: Un nom conventionnel pour le premier paramètre des méthodes d’instance, représentant l’instance sur laquelle la méthode est appelée.- Héritage : Un mécanisme par lequel une nouvelle classe (enfant/sous-classe) dérive des attributs et des méthodes d’une classe existante (parent/super-classe). Favorise la réutilisation du code.
- Encapsulation : Le regroupement des données (attributs) et des méthodes qui opèrent sur ces données au sein d’une seule unité (classe). Implique souvent le contrôle de l’accès aux détails internes (par exemple, en utilisant des attributs privés).
- Polymorphisme : La capacité d’objets de classes différentes à répondre au même appel de méthode d’une manière spécifique à leur type de classe (‘formes multiples’).
Création de classes
Section titled “Création de classes”Utilisez le mot-clé class suivi du nom de la classe (généralement en CamelCase) et d’un double point. Le bloc indenté contient la définition de la classe.
class ClassName: """Docstring optionnel décrivant la classe.""" # Corps de la classe : attributs et méthodes pass # Utilisez 'pass' pour un corps de classe videExemple : Une classe Dog simple
Section titled “Exemple : Une classe Dog simple”class Dog: """Une représentation simple d'un chien."""
# Variable de classe (partagée par toutes les instances de Dog) species = "Canis familiaris"
def __init__(self, name, age): """Initialise le nom et l'âge du chien.""" # Variables d'instance (uniques à chaque instance) self.name = name self.age = age print(f"Chien nommé {self.name} créé !")
# Méthode d'instance def bark(self): """Simule l'aboiement du chien.""" print(f"{self.name} dit : Ouah !")
# Une autre méthode d'instance def describe(self): """Décrit le chien.""" print(f"{self.name} a {self.age} ans.")Points clés :
Création d’objets d’instance
Section titled “Création d’objets d’instance”Pour créer un objet (instance) d’une classe, appelez le nom de la classe comme s’il s’agissait d’une fonction, en passant tous les arguments requis par la méthode __init__ (à l’exclusion de self).
# Crée deux objets Dogmy_dog = Dog("Buddy", 3)your_dog = Dog("Lucy", 5)Sortie de __init__ :
Chien nommé Buddy créé !Chien nommé Lucy créé !Accès aux attributs et appel de méthodes
Section titled “Accès aux attributs et appel de méthodes”Utilisez l’opérateur point (.) pour accéder aux attributs (variables d’instance) d’un objet et appeler ses méthodes.
# Accéder aux variables d'instanceprint(f"{my_dog.name} a {my_dog.age} ans.")
# Appeler les méthodes d'instancemy_dog.bark()your_dog.describe()
# Accéder à la variable de classe (peut être accédée via l'instance ou la classe)print(f"Buddy appartient à l'espèce : {my_dog.species}")print(f"Tous les chiens appartiennent à : {Dog.species}")Sortie :
Buddy a 3 ans.Buddy dit : Ouah !Lucy a 5 ans.Buddy appartient à l'espèce : Canis familiarisTous les chiens appartiennent à : Canis familiarisModification des attributs
Section titled “Modification des attributs”Vous pouvez modifier les attributs d’instance directement (s’ils ne sont pas destinés à être privés). Vous pouvez également ajouter de nouveaux attributs à des instances spécifiques dynamiquement.
my_dog.age = 4 # Modifie un attribut existantmy_dog.breed = "Golden Retriever" # Ajoute un nouvel attribut uniquement à my_dog
print(f"Âge mis à jour de Buddy : {my_dog.age}")print(f"Race de Buddy : {my_dog.breed}")
# your_dog n'a pas l'attribut 'breed' initialement# print(your_dog.breed) # Lèverait une AttributeError
# La modification des variables de classe affecte toutes les instancesDog.species = "A good doggo"print(f"Espèce de Lucy maintenant : {your_dog.species}")Fonctions d’attributs intégrées :
getattr(obj, name[, default]): Accède à l’attributnamedeobj. Renvoiedefaultsi spécifié et si l’attribut n’existe pas, sinon lève uneAttributeError.hasattr(obj, name): RenvoieTruesiobjpossède un attributname,Falsesinon.setattr(obj, name, value): Définit l’attributnamedeobjàvalue. Crée l’attribut s’il n’existe pas.delattr(obj, name): Supprime l’attributnamedeobj.
Attributs de classe intégrés
Section titled “Attributs de classe intégrés”Les classes Python possèdent plusieurs attributs intégrés, accessibles via la notation par points :
ClassName.__doc__: La chaîne de documentation de la classe (ouNone).ClassName.__name__: Le nom de la classe.ClassName.__module__: Le nom du module où la classe a été définie.ClassName.__bases__: Un tuple contenant les classes parentes (classes de base).ClassName.__dict__: Un dictionnaire contenant l’espace de noms de la classe (variables de classe, méthodes, etc.).instance.__class__: La classe à laquelle une instance appartient.
print(f"Dog.__doc__ : {Dog.__doc__}")print(f"Dog.__name__ : {Dog.__name__}")print(f"Dog.__module__ : {Dog.__module__}")print(f"Dog.__bases__ : {Dog.__bases__}") # Tuple vide s'il n'y a pas d'héritage explicite# print(f"Dog.__dict__ : {Dog.__dict__}") # Affiche les attributs/méthodes de la classeprint(f"my_dog.__class__ : {my_dog.__class__}")Collecte des déchets et destructeurs (__del__)
Section titled “Collecte des déchets et destructeurs (__del__)”Python gère automatiquement la mémoire en utilisant la collecte des déchets (garbage collection), principalement basée sur le comptage de références. Lorsqu’un objet n’a plus aucune référence (aucune variable ne le pointe), Python récupère sa mémoire.
Vous pouvez définir une méthode spéciale __del__(self), appelée destructeur, que Python tente d’appeler juste avant qu’un objet ne soit collecté par le garbage collector. Elle est utilisée pour les tâches de nettoyage comme la fermeture de fichiers ou de connexions réseau associées à l’objet.
Attention : S’appuyer sur __del__ peut être délicat en raison du moment de la collecte des déchets et des problèmes potentiels de références circulaires. Il est souvent préférable d’utiliser des gestionnaires de contexte (instruction with) pour la gestion des ressources.
class FileHandler: def __init__(self, filename): print(f"Ouverture de {filename}") self.filename = filename # self.file = open(filename, 'w') # Exemple de ressource
def __del__(self): # Actions de nettoyage print(f"Destructeur appelé pour {self.filename}. Nettoyage en cours.") # if hasattr(self, 'file') and not self.file.closed: # self.file.close()
fh = FileHandler("my_data.txt")# Plus tard, lorsque 'fh' n'est plus référencé...del fh # Déclenche manuellement la baisse du compte de références# ... la collecte des déchets appelle finalement __del__Héritage de classes
Section titled “Héritage de classes”L’héritage permet à une classe (enfant/sous-classe) d’hériter des attributs et des méthodes d’une autre classe (parente/super-classe). Syntaxe : class ChildClass(ParentClass):
Exemple :
Section titled “Exemple :”class Animal: def __init__(self, name): self.name = name print("Animal créé")
def speak(self): print("Son animal générique")
def eat(self): print(f"{self.name} mange.")
# Cat hérite de Animalclass Cat(Animal): def __init__(self, name, breed): print("Constructeur de Cat appelé") # Appelle la méthode __init__ de la classe parente super().__init__(name) self.breed = breed
# Surcharge la méthode speak def speak(self): print(f"{self.name} dit : Miaou !")
# Ajoute une nouvelle méthode spécifique à Cat def purr(self): print(f"{self.name} ronronne.")
# Crée une instance de la classe enfantmy_cat = Cat("Whiskers", "Siamese")
my_cat.eat() # Méthode héritée d'Animalmy_cat.speak() # Méthode surchargée dans Catmy_cat.purr() # Méthode spécifique à Catprint(f"Race : {my_cat.breed}")Sortie :
Constructeur de Cat appeléAnimal crééWhiskers mange.Whiskers dit : Miaou !Whiskers ronronne.Race : Siamesesuper().__init__(...) est utilisé pour appeler l’initialiseur de la classe parente.
Héritage multiple : Une classe peut hériter de plusieurs parents : class Child(Parent1, Parent2):. Python utilise l’Ordre de Résolution des Méthodes (MRO) pour déterminer quelle méthode parente appeler en cas de conflits (algorithme de linéarisation C3).
Surcharge de méthodes
Section titled “Surcharge de méthodes”Une classe enfant peut fournir sa propre implémentation d’une méthode héritée d’une classe parente. C’est ce qu’on appelle la surcharge (ou overriding). Voir la méthode speak dans l’exemple Cat ci-dessus.
Méthodes spéciales (Magiques/Dunder)
Section titled “Méthodes spéciales (Magiques/Dunder)”Les classes Python peuvent définir des méthodes spéciales avec des doubles underscores (__methodname__) qui permettent aux objets d’interagir avec les opérations Python intégrées (comme l’addition, l’impression, la comparaison).
| Méthode | Opérateur/Fonction | Description |
|---|---|---|
__init__(self, ...) | Création d’objet | Initialiseur |
__str__(self) | str(), print() | Représentation informelle, lisible par l’homme |
__repr__(self) | repr() | Représentation officielle, non ambiguë (souvent du code pour recréer l’objet) |
__len__(self) | len() | Longueur de l’objet |
__add__(self, other) | + | Addition |
__eq__(self, other) | == | Comparaison d’égalité |
__lt__(self, other) | < | Comparaison “inférieur à” |
__getitem__(self, key) | obj[key] | Accéder à un élément par clé (indexation/recherche) |
__call__(self, ...) | obj(...) | Rendre l’objet appelable comme une fonction |
Exemple (Surcharge de + et __str__) :
Section titled “Exemple (Surcharge de + et __str__) :”class Vector: def __init__(self, x, y): self.x = x self.y = y
def __add__(self, other): """Définit l'addition vectorielle en utilisant l'opérateur +.""" if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) return NotImplemented # Indique que l'opération n'est pas supportée
def __str__(self): """Définit la façon dont le vecteur est affiché.""" return f"Vector({self.x}, {self.y})"
def __repr__(self): """Définit la représentation 'officielle'.""" return f"Vector(x={self.x}, y={self.y})"
v1 = Vector(2, 3)v2 = Vector(5, -1)v3 = v1 + v2 # Utilise la méthode __add__
print(v1) # Utilise __str__print(v3) # Utilise __str__print(repr(v3)) # Utilise __repr__Sortie :
Vector(2, 3)Vector(7, 2)Vector(x=7, y=2)Masquage des données (Name Mangling)
Section titled “Masquage des données (Name Mangling)”Python n’a pas d’attributs ‘privés’ stricts comme certains langages. Cependant, en préfixant un nom d’attribut avec deux underscores (par exemple, __secret), Python effectue un name mangling. Il modifie le nom de l’attribut en interne en _ClassName__attributeName. Cela rend son accès accidentel plus difficile (mais pas impossible) depuis l’extérieur de la classe, évitant ainsi les conflits de noms dans les sous-classes.
class MySecret: def __init__(self): self.public_var = 10 self.__secret_var = 20 # Nom *manglé*
def get_secret(self): return self.__secret_var
secret_obj = MySecret()print(secret_obj.public_var) # L'accès public est correct
# print(secret_obj.__secret_var) # AttributeError !
# Accès via le nom manglé (déconseillé sauf nécessité)print(secret_obj._MySecret__secret_var)
print(secret_obj.get_secret()) # L'accès via méthode est préféréSortie :
102020Conventionnellement, un préfixe à un seul underscore (_variable) signale qu’un attribut ou une méthode est destiné à un usage interne, mais ce n’est pas appliqué par Python.
Propriétés et classes de données
Section titled “Propriétés et classes de données”Python moderne offre des fonctionnalités comme @property pour créer des attributs gérés (méthodes getter/setter/deleter déguisées en simple accès aux attributs) et @dataclass (du module dataclasses) pour réduire le code répétitif lors de la création de classes principalement destinées à stocker des données.