Aller au contenu
Accueil » Un premier pas vers la programmation orientée objet en Python

Un premier pas vers la programmation orientée objet en Python

    Bonjour à tous, dans cet article nous aborderons les principaux concepts de la programmation orientée objet (POO) en Python. Que vous fassiez vos premiers avec ce type de programmation ou que vous êtes déjà familier avec la POO, cet article reprend les basiques. La programmation orientée objet est une méthode de conception et d’organisation de votre code qui vous permettra de créer des variables structurées.

    La programmation orientée objet ou assembler les différentes pièces de votre programme

    Cette approche est particulièrement populaire pour sa flexibilité, sa modularité et sa capacité à modéliser des situations complexes de manière intuitive. En effet, les variables de base ne permettent pas de modéliser des objets complexes de la vie de tous les jours, comme une voiture par exemple. En programmation orientée objet, vous pourrez alors définir l’objet que vous voudrez.

    Un objet est composé de méthodes (fonctions) et d’attributs (variables). Ces premières sont comme des fonctions associées à l’objet. Quant aux attributs, ce sont des variables assignées à l’objet, la valeur de la variable lui est propre. Nous verrons plus en détails ces points particuliers mais tout d’abord, voici comment déclarer un objet en Python.

    Déclaration d’un objet en Python

    En python pour déclarer un objet, il faut utiliser l’instruction « class ». En effet, en programmation Python, un objet est défini par une classe. Chaque objet est alors une instance de cette classe.

    class MonSuperObjet:
        """ Description de l'objet """
        pass
    
    objet1 = MonSuperObjet()
    objet2 = MonSuperObjet()
    
    print(objet1)
    print(objet2)
    
    >>> <__main__.MonSuperObjet object at 0x0000015CDF47F950>
    >>> <__main__.MonSuperObjet object at 0x0000015CDF47F7D0>
    Python

    La classe « MonSuperObjet » a été instanciée deux fois, dans les variables « objet1 » et « objet2 ». Quand on affiche ces deux variables, on voit qu’elles sont toutes les deux des instances de la classe de « MonSuperObjet » mais qu’elles sont stockées à deux adresses différentes dans la mémoire. Chaque objet va alors hériter des méthodes et des attributs de classe.

    Méthodes et attributs

    Comme évoqué dans l’introduction, un objet possède des méthodes et des attributs. Ceux-ci sont définis au sein de la classe elle-même.

    Un attribut correspond à une variable mais cette variable appartient à l’objet. Il est lié intimement à cet objet. Concrètement, cela veut dire que pour deux objets issus de la même classe, la variable peut avoir deux valeurs différentes. Nous allons voir cela dans l’exemple suivant.

    Une méthode correspond à une fonction au sein d’une classe. Comme les attributs, elles sont liées à l’objet. Une méthode permet de déclencher une action propre à la classe de l’objet.

    Lorsque que vous créerez votre objet, celui-ci sera donc une instance de la classe concernée. Lors de la création, l’objet va alors hériter des attributs et des méthodes associés à cette classe. Voici un exemple :

    class voiture:
        nb_roues = 4
        
        def demarrer(self):
            print("C'est parti !")
    
    vehicule1 = voiture()
    vehicule2 = voiture()
    
    print(f"Le véhicule 1 a {vehicule1.nb_roues} roues.")
    print(f"Le véhicule 2 a {vehicule2.nb_roues} roues.")
    
    vehicule2.nb_roues = 3
    print("Le vehicule 2 a perdu une roue.")
    print(f"Le véhicule 1 a {vehicule1.nb_roues} roues.")
    print(f"Le véhicule 2 a {vehicule2.nb_roues} roues.")
    
    vehicule1.demarrer()
    
    >>> Le véhicule 1 a 4 roues.
    >>> Le véhicule 2 a 4 roues.
    >>> Le vehicule 2 a perdu une roue.
    >>> Le véhicule 1 a 4 roues.
    >>> Le véhicule 2 a 3 roues.
    >>> C'est parti !
    
    Python

    L’exemple précédemment montre comment utiliser les arguments d’un objet mais également l’associativité des arguments avec un objet. En effet, nous avons modifié le nombre de roues pour le véhicule 2 et cela n’a eu aucun impact sur le véhicule 1. Une fois créé, les objets sont donc indépendants.

    De plus, nous avons vu comment appeler la méthode appartenant à la classe de l’objet. La syntaxe est toujours la même : « variable.méthode() ». Cette instruction aura pour effet de déclencher de la méthode et donc toutes les actions associées. La méthode étant associée à la classe, vous êtes donc sûr que le nom de la méthode sera toujours le même.

    Ces premiers exemples vous donne un avant-goût de la puissance des objets. Ils sont comme une super variable dans laquelle vous pouvez définir le comportement que vous souhaitez.

    La méthode __init__

    Le constructeur, repérable par l’instruction « __init__ », est une méthode spéciale qui est appelée lors de la création d’un nouvel objet. Il est utilisé pour initialiser la valeur des attributs de la classe. Votre programme devra alors envoyer les arguments nécessaires pour la bonne réalisation de l’initialisation. Si vous ne donnez pas le bon nombre d’argument, votre code renverra une erreur.

    class Livre: 
        def __init__(self, titre, auteur, isbn): 
            self.titre = titre 
            self.auteur = auteur 
            self.isbn = isbn 
            self.disponible = True 
    class Utilisateur: 
        def __init__(self, nom, email): 
            self.nom = nom 
            self.email = email 
    class Transaction: 
        def __init__(self, livre, utilisateur): 
            self.livre = livre 
            self.utilisateur = utilisateur 
            self.date = None 
        def emprunter(self, date): 
            if self.livre.disponible: 
                self.livre.disponible = False 
                self.date = date
                print("Livre emprunté.")
            else: 
                print("Livre indisponible")
    
    user1 = Utilisateur("Benjamin", "ben.ja@min.fr")
    user2 = Utilisateur("Olivier", "oli@vier.fr")
    livre1 = Livre("Apprendre Python", "Benjamin", 123456)
    transaction = Transaction(livre1, user1)
    transaction.emprunter("01/01/2025")
    transaction = Transaction(livre1, user2)
    transaction.emprunter("01/02/2025")
    
    >>> Livre emprunté.
    >>> Livre indisponible
    
    Python

    L’exemple présente un cas d’application pour gérer une bibliothèque. Trois classes sont utilisées. Les deux premières sont utilisées pour modéliser l’objet « livre » et la seconde pour modéliser la personne qui emprunte. La troisième quant à elle sert à modéliser la transaction. Si l’utilisateur souhaite emprunter le livre, la classe va vérifier si le livre en question est disponible ou non. L’instruction « __init__ » permet donc d’initialiser chaque classe avec les informations qui leur est propre.

    user3 = Utilisateur("Test") # Il manque l'argument de l'adresse email
    
    >>> TypeError: Utilisateur.__init__() missing 1 required positional argument: 'email'
    Python

    Cependant, si vous instanciez une classe sans donner le bon nombre d’arguments, vous obtiendrez un erreur telle qu’illustrée dans l’exemple ci-dessus.

    Quelques principes de la programmation orientée objet

    L’encapsulation

    L’encapsulation est le concept de regrouper les données (variables) et les méthodes (fonctions) qui les manipulent en une seule unité appelée « objet ». Cette encapsulation permet de cacher les détails internes de l’objet et de n’exposer que les fonctionnalités nécessaires à l’extérieur. En d’autres termes, cela signifie que les données d’un objet ne peuvent être modifiées qu’à travers des méthodes spécifiques de cet objet, assurant ainsi la sécurité et la cohérence des données.

    Pour utiliser d’autres termes, ces variables et méthodes sont privées et ne sont accessibles qu’à l’intérieur de l’objet. Cela empêche que d’autres classes ou fonctions viennent modifier ces données. Cela permet également de « cacher » certaines données du reste du programme.

    class Voiture:
        def __init__(self, marque, modele):
            self.marque = marque
            self.modele = modele
            self.__vitesse = 0  
            # Utilisation du double underscore pour encapsuler la vitesse
    
        def accelerer(self, increment):
            self.__vitesse += increment
    
        def afficher_vitesse(self):
            print(f"Vitesse : {self.__vitesse} km/h")
    
    # Création d'un objet
    ma_voiture = Voiture("Peugeot", "308")
    
    # Affichage de la vitesse initiale
    ma_voiture.afficher_vitesse()
    
    # Tentative de modifier la vitesse
    ma_voiture.__vitesse = 200
    ma_voiture.afficher_vitesse()
    
    # Modification de la vitesse via une méthode
    ma_voiture.accelerer(30)
    
    # Accès à la vitesse uniquement via une méthode
    ma_voiture.afficher_vitesse()
    
    >>> Vitesse : 0 km/h
    >>> Vitesse : 0 km/h
    >>> Vitesse : 30 km/h
    Python

    L’exemple précédent illustre parfaitement l’encapsulation. La variable « vitesse » n’est accessible qu’un sein de l’objet lui-même. Pour modifier la vitesse, vous n’avez pas d’autres choix que de passer par la méthode appropriée, à savoir la méthode « accelerer » dans notre exemple.

    L’héritage d’une classe en Python

    L’héritage est un mécanisme qui permet à une classe (appelée classe dérivée ou sous-classe) d’hériter des propriétés et des méthodes d’une autre classe (appelée classe de base ou classe parent ou « superclass »). Cela favorise la réutilisation du code et la création de hiérarchies de classes. Les classes dérivées peuvent ajouter des fonctionnalités supplémentaires ou modifier le comportement des méthodes héritées, mais elles héritent également de la structure et des fonctionnalités de la classe parent.

    L’héritage sera donc très utile pour subdiviser nos objets. Voici un exemple avec la gente animale. Dans un premier temps, il faut définir la classe globale. Ensuite, vous pourrez créer des sous-classes en rappelant entre parenthèses la classe parent.

    class Animal:
        def __init__(self, nom):
            self.nom = nom
    
    class Chien(Animal):
        def parler(self):
            return "Woof!"
    
    class Chat(Animal):
        def parler(self):
            return "Miaou!"
    
    chien = Chien("Rex")
    chat = Chat("Mistigri")
    
    print(chien.nom, "dit:", chien.parler())
    print(chat.nom, "dit:", chat.parler())
    
    >>> Rex dit: Woof!
    >>> Mistigri dit: Miaou!
    
    Python

    Pour modéliser un chat ou un chien, nous sommes donc passés par une classe globale qui est la classe animale. Dans notre notre exemple, le seul attribut en commun est le nom de l’animal. Ensuite, l’instanciation de la classe « Chat » ou « Chien » va rajouter des attributs et méthodes propre à la sous-classe en question. Dans notre cas, cela sera la méthode « parler ».

    Le polymorphisme

    Le polymorphisme consiste à utiliser l’héritage en plus de l’héritage, le polymorphisme modifie une méthode ou un attribut de la classe de base pour ajuster ceux-ci à la sous-classe en question. Ainsi, un attribut ou une méthode peuvent déjà exister dans la classe parent. Ils seront donc hérités par les classes enfant. Cependant, cette classe enfant peut écraser ces attributs ou méthodes existants afin de changer le comportement de base. C’est ce qu’on appelle le polymorphisme.

    Le polymorphisme favorise la flexibilité du code en permettant de créer des méthodes génériques fonctionnant avec un large éventail d’objets. L’exemple suivant reprend le précédent. On rajoute à la classe de base la méthode « parler ». Cela veut dire que chaque animal créé aura la possibilité de parler suivant cette méthode. Ensuite, via l’héritage et le polymorphisme, cette méthode « parler » va être modifiée suivant le type de l’animal.

    class Animal:
        def __init__(self, nom):
            self.nom = nom
    
        def parler(self):
            return "Son d'un animal inconnu"
    
    class Chien(Animal):
        def parler(self):
            return "Woof!"
    
    class Chat(Animal):
        def parler(self):
            return "Miaou!"
    
    animal = Animal("Pukie")
    chien = Chien("Rex")
    chat = Chat("Mistigri")
    
    print(animal.nom, "dit:", animal.parler())
    print(chien.nom, "dit:", chien.parler())
    print(chat.nom, "dit:", chat.parler())
    
    >>> Pukie dit: Son d'un animal inconnu
    >>> Rex dit: Woof!
    >>> Mistigri dit: Miaou!
    
    Python

    Pour résumer …

    Dans cet article vous avez pu apprendre à créer des objets mais également des interactions entre eux. Cela est particulièrement puissant pour créer du code flexible et modulable. Ainsi, vous pourrez décomposer votre programme en différents blocs simples qui, une fois combinés, formeront un système complexe.

    En utilisant au mieux les différents concepts vus dans cet article, à savoir l’encapsulation, l’héritage et le polymorphisme, vous avez les clés pour commencer à créer des projets complexes en utilisant des méthodes appliquées par les professionnels.

    Avec l’encapsulation, vous pourrez définir des attributs qui ne seront pas accessibles depuis l’extérieur. Ainsi, il est possible de figer certaines valeurs ou méthodes afin de garantir que rien ne viendra modifier la classe pour ces éléments précis.

    L’héritage et le polymorphisme sont des outils puissants vous permettant de décomposer vos classes. Si votre programme traite d’animaux, vous pourrez alors définir plusieurs classes suivant le niveaux de classification dont vous avez besoin (espèce, race, mammifère, …). A chaque sous-classe, des méthodes et attributs spécifiques se rajouteront tout en héritant des caractéristiques générales de la classe supérieure.

    L’abstraction n’a pas été mentionné ici. Cela fera l’objet d’un autre article. L’idée de ce premier billet est d’introduire les notions de la programmation objet et sentir les possibilités qu’elle apporte. J’espère que cet article vous a permis de mieux appréhender la programmation orientée objet en python.

    Pour plus d’informations sur les classes en Python, voici le lien vers la documentation officielle. Si vous souhaitez laisser un commentaire, n’hésitez pas, j’y répondrai avec plaisir.

    Si vous avez aimé l'article, vous êtes libres de le partager ! :)

    Laisser un commentaire