Aller au contenu

TNSI : cours POO - Programmation Orienté Objet (OOP)⚓︎

Introduction⚓︎

La POO est une nouvelle (pour nous cette année) méthode de programmation, on parle de nouveau paradigme de programmation, basé autour du concept central d'Objet.

Intuitivement, un Objet est la modélisation de la réponse à une question de la forme :

  • de quoi parle-t-on? (de quel objet matériel)
  • de qui parle-t-on? (de quel objet vivant)
  • de quel concept/objet intelectuel abstrait parle-t-on?

On pourra par la suite définir plus précisément ce que l'on peut en faire.

Définitions⚓︎

Objets⚓︎

Le concept d' Objet est inspiré intuitivement de la vie réelle et permet par exemple de modéliser d'une manière très générale :

  • une matérialisation spécifique unique d'un objet matériel
  • une incarnation spécifique unique d'une forme de vie
  • une instance spécifique unique d'un concept intelectuel et/ou abstrait

Un Objet au sens de la POO, peut modéliser respectivement :

  • Un objet matériel précis :
  • La voiture de mon cousin Laurent, peut être vue comme un Objet (au sens de la POO).
  • Le vélo bleu de ma fille
  • Un être vivant précis :
  • Mon cousin Julien, peut être considéré comme un Objet (au sens POO)
  • mon chat nommé Mishka
  • Un exemplaire/une instance précis(e) d'un concept intelectuel et/ou abstrait :
  • Mon compte Bancaire personnel, sur le site de ma banque, peut être vu comme un Objet (au sens POO)
  • Le tableau de mes notes de Maths au T1 de cette année
  • Le graphe modélisant les maisons et les rues de ma résidence

Classes et Instances⚓︎

  • Par opposition à un Objet précis, une Classe est une modélisation abstraite d'un ensemble d'Objets de même Catégorie, disposant de certaines caractéristiques communes.
  • De manière plus appliquée, une Classe peut être vue/utilisée comme une sorte de moule/usine à fabriquer des Objets, appelés des instances de la Classe:
  • Au lieu de dire qu'on fabrique un objet d'une certaine Classe/Catégorie, on préfère dire que: on obtient un Objet en instanciant la Classe (/la Classe a été instanciée)
  • l'Objet ainsi créé est une instance de la Classe

Une Classe, au sens de la POO, est un moule/usine à fabriquer des Objets de types/genre divers :

  • des Objets matériels :
  • La Classe Voiture modélise le concept général de Voiture.
    • La Classe Voiture est une usine à fabriquer des Objets voitures: on instancie la Classe Voiture pour fabriquer un Objet voiture.
    • L'Objet "voiture de mon cousin Laurent" est une instance de la Classe Voiture.
    • L'Objet "voiture de mon amie Julie" est une autre instance de la Classe Voiture.
  • La Classe Velo modélise le concept général de Vélo.

    • La Classe Velo est une usine à fabriquer des Objets vélo: on instancie la Classe Velo pour fabriquer un Objet vélo.
    • L'Objet "vélo de ma fille" est une instance de la Classe Velo.
    • L'Objet "vélo de Farid" est une autre instance de la Classe Velo.
  • des Objets modélisant des Êtres vivants :

  • La Classe Personne modélise le concept général de Personne.
    • L'Objet "Sophie" est une instance de la Classe Personne.
    • L'Objet "Paul" est une autre instance de la Classe Personne.
  • La Classe Chat modélise le concept général de Chat.
    • L'Objet "mon chat Mishka" est une instance de la Classe Chat.
    • L'Objet "Le Chat de ma voisine Laura" est une autre instance de la Classe Chat.
  • des Objets modélisant un concept intelectuel et/ou abstrait :
  • La Classe Compte (Bancaire) modélise le concept général de compte bancaire.
    • L'Objet "Mon compte Bancaire personnel" est une instance de la Classe Compte
    • L'Objet "Ton compte Bancaire personnel" est une autre instance de la Classe Compte
  • La Classe Tableau modélise le concept général de Tableau.

    • L'Objet "Tableau de mes notes de Maths au T1 cette année" est une instance de la Classe Tableau
    • L'Objet "Tableau des Témpératures quotidiennes à 12h, à mon adresse, en Octobre" est une autre instance de la Classe Tableau
  • La Classe Graphe modélise le concept général de Graphe (réunion de Sommets dont certains sont reliés entre eux par des Arcs).

    • L'Objet "Le graphe modélisant les maisons et les rues de ma résidence" est une instance de la Classe Graphe
    • L'Objet "Le graphe modélisant les connexions filaires de mon réseau LAN" est une autre instance de la Classe Graphe

Attributs & Méthodes⚓︎

Attributs & Méthodes⚓︎

Une Classe modélise un ensemble d'Objets d'une même catégorie, ayant certaines caractéristiques communes :

  • des attributs ou champs : ce sont des caractéristiques statiques, descriptives :

    • de l'état d'un objet, et/ou
    • de ses liens avec le monde extérieur (de la Classe)

    En pratique, les attributs sont des variables

  • des méthodes : ce sont des caractéristiques dynamiques, d'(inter)actions/opérations possibles par/avec cet objet. En pratique, ce sont des fonctions.

Remarque

  • Les attributs que nous considérerons sont (à priori) distinctes pour chaque Objet distinct: ce sont donc des variables qui dépendent de chaque instance (distincte). C'est ce qu'on appelle des variables d'instance. En pratique pour nous, dans toute la suite, les Attributs seront des variables d'instances.
  • CULTURE : Pour information, il est possible de définir des attributs qui prennent obligatoirement la même valeur pour TOUTES les instances de la classe (pour tous les objets distincts), et sont donc indépendantes des instances, mais dépendent exclusivement de la Classe: on les appelle des variables de classe. Nous ne les utiliserons pas.

Signature d'une méthode⚓︎

La Signature (de type) d'une méthode est composée de:

  • du nom de ses paramètres
  • des types de données des paramètres en entrée
  • du type de données de la valeur de retour/sortie (quel type de données renvoie le return ?)

Exemples⚓︎

Quelques exemples possibles d'Attributs et Méthodes disponibles pour les (Objets des) Classes suivantes :

1⃣ Classes d' Objets matériels :

  • Classe Voiture :

    • Attributs pour un objet de Classe Voiture :

      • la variable stockant sa marque
      • la variable stockant son modèle
      • la variable stockant son poids
      • la variable stockant son annee_de_mise_en_circulation
      • la variable stockant sa couleur
      • la variable stockant sa nombre_de_portes
    • Méthodes pour un objet de Classe Voiture :

      • la fonction depasser()
      • la fonction klaxonner()
      • la fonction se_garer()
      • la fonction gonfler_pneus()
  • Classe Velo :

    • Attributs pour un objet de Classe Velo :

      • la variable stockant sa marque
      • la variable stockant son modèle
      • la variable stockant son poids
      • la variable stockant son annee_fabrication
      • la variable stockant sa couleur
      • la variable stockant si OUI ou NON il dispose d'une bequille
    • Méthodes pour un objet de Classe Velo :

      • la fonction depasser()
      • la fonction klaxonner()
      • la fonction attacher_cadenas()
      • la fonction remettre_chaine()
      • la fonction gonfler_pneus()

2⃣ Classes d' Objets modélisant des Êtres vivants :

  • Classe Personne :

    • Attributs pour un objet de Classe Personne :

      • la variable stockant sa taille
      • la variable stockant son poids
      • la variable stockant sa date_naissance
      • la variable stockant sa couleur_cheveux
      • la variable stockant sa couleur_yeux
      • la variable stockant son metier
      • la variable stockant son niveau_d_etudes
    • Méthodes pour un objet de Classe Personne :

      • la fonction manger()
      • la fonction parler()
      • la fonction chanter()
      • la fonction dormir()
      • la fonction faire_les_courses()
      • la fonction travailler()
      • la fonction apprendre()
  • Classe Chat :

    • Attributs pour un objet de Classe Chat :

      • la variable stockant sa taille
      • la variable stockant son poids
      • la variable stockant sa date_naissance
      • la variable stockant sa couleur_pelage
      • la variable stockant sa couleur_yeux
      • la variable stockant son proprietaire
      • la variable stockant son nombre_moustaches
    • Méthodes pour un objet de Classe Chat :

      • la fonction manger()
      • la fonction parler()
      • la fonction chanter()
      • la fonction dormir()
      • la fonction chasser()
      • la fonction griffer()

3⃣ Classes d'Objets modélisant un concept intelectuel et/ou abstrait :

  • Classe Compte :

    • Attributs pour un objet de Classe Compte :

      • la variable stockant le nom_proprietaire du compte
      • la variable stockant le numero de compte
      • la variable stockant son solde
      • la variable stockant sa date_de_creation
    • Méthodes pour un objet de Classe Compte :

      • la fonction faire_virement()
      • la fonction ajouter_destinataire()
      • la fonction demander_carte_bleue()
  • Classe Tableau :

    • Attributs pour un objet de Classe Tableau :

      • la variable stockant le nombre_d_elements du Tableau
    • Méthodes pour un objet de Classe Tableau :

      • la fonction minimum_des_elements() du Tableau
      • la fonction maximum_des_elements()
      • la fonction trier_les_elements_ordre_croissant()
      • la fonction extraire_premier_element()
      • la fonction extraire_dernier_element()
      • la fonction additionner_tous_les_elements()
      • la fonction moyenne_tous_les_elements()
  • Classe Graphe :

    • Attributs pour un objet de Classe Graphe :

      • la variable stockant le nombre_de_sommets du Graphe
      • la variable stockant le nombre_d_arcs
    • Méthodes pour un objet de Classe Graphe :

      • la fonction liste_sommets() du Graphe
      • la fonction ajoute_sommet()
      • la fonction supprime_sommet()
      • la fonction liste_arcs()
      • la fonction ajoute_arc()
      • la fonction supprime_arc()
      • la fonction degre()

Attributs distincts pour Objets distincts⚓︎

Pte

Les différentes instances d'une même classe ne différent entre elles que par les différences entre les valeurs de leurs Attributs.

Interface⚓︎

Méthodes Publiques vs méthodes Privées⚓︎

Certaines méthodes (pas forcément toutes) constituent la partie visible de l'objet (depuis l'extérieur de la Classe: e.g. depuis un autre objet, ou plus généralement depuis la partie principale de l'algorithme).

méthode publique vs méthode privée

  • Une méthode accessible depuis l'extérieur de la Classe est appelée une méthode publique. Pour qu'une méthode soit définie comme publique, certains langages utilisent le mot-clé public 🇬🇧 lors de la définition de la méthode : c'est ce que l'on appelle un modificateur d'accès ou un access modifier 🇬🇧.
  • Une méthode accessible uniquement depuis l'intérieur de la Classe est appelée une méthode privée. Pour qu'une méthode soit défiie comme privée, certains langages utilisent le modificateur d'accès private 🇬🇧 lors de la définition de la méthode.

Remarque Certains langages (Java, C++, etc...) utilisent également des méthodes protégées, qui par définition, sont accessibles depuis la classe, et/ou depuis les sous-classes (classes enfants) de cette classe. Pour qu'une méthode soit définie comme protégée, certains langages utilisent le modificateur d'accès protected 🇬🇧

Interface & Méthodes Publiques⚓︎

C'est au travers de ces méthodes publiques que l'on s'adresse à l'Objet depuis l'extérieur de la Classe :

Interface & méthodes publiques

L'ensemble des signatures de toutes ces méthodes publiques s'appelle une Interface (de programmation). L'interface de programmation permet donc de savoir comment utiliser un objet? Que peut-on faire avec lui? Que peut-on lui demander?

Attributs Publics, Privés, Protégés. Encapsulation⚓︎

Dans certains langages (Javascript, Java, C++, PHP, etc..) les attributs d'un Objet peuvent être:

  • public
  • private
  • protected

Encapsulation

Cela permet que certains attributs soient inaccessibles, partiellement ou totalement, depuis l'extérieur de la Classe (en particulier depuis d'autres objets): dans ce cas on parle d'Encapsulation

en Python

Dans le langage Python, on utilise :

  • deux caractères underscore __ devant le nom de l'attribut, pour dire que l'attribut est privé / private
  • un seul caractère underscore _ devant le nom de l'attribut, pour dire que l'attribut est protégé / protected
nomObjet.__attributPrive = 2
nomObjet._attributProtege = "Bonjour"

Remarquez qu'en Python, l'utilisation d'un unique underscore _ pour définir l'attribut comme protégé :

  • est une simple convention de nommage, en particulier :
  • Python n'empêche ni l'accès à la variable protégée, ni sa modification... ce qui est anti-intuitif

Par contre, l'utilisation d'un double underscore __ est plus restrictive: Toute tentative d'accès à la variable d'instance __attributPrive de l'objet nomObjet depuis l'extérieur, se soldera par une erreur:

AttributeError: 'nomClasse' object has no attribute '__attributPrive'

Langage Python⚓︎

Pour créer des Objets, il FAUT OBLIGATOIREMENT, AU PRÉALABLE, avoir défini une Classe.

Le mot-clé self⚓︎

Une fois un Objet créé en ayant instancié la Classe souhaitée, il semble légitime de souhaiter/pouvoir accéder aux attributs et méthodes de cet Objet courant :

  • depuis l'intérieur de la Classe : par exemple, accéder à un attribut et/ou à une méthode, depuis une autre méthode de la Classe
  • depuis l'extérieur de la Classe (ou bien, souhaiter que ce ne soit pas possible, par sécurité, dans certains contextes)

Pour résoudre ce genre de problèmes, on est amené à inventer et utiliser un mot-clé, self, qui fait référence à l'instance courante de la Classe. Autrement dit, self (=soi-même🇫🇷) fait référence à l'instance de l'Objet courant qui souhaite accéder aux attributs et aux méthodes.

Remarque D'autres langages utilisent d'autres mot-clés à la place de self:

  • this en JavasScript, Java, C++
  • $this en PHP
  • @ en Ruby, etc..

Le Constructeur __init__()⚓︎

Il semble légitime de souhaiter/devoir/pouvoir initialiser un objet, lors de sa création, avec des valeurs par défaut. On utilise pour cela la fonction magique __init__() qui prendra en argument le mot-clé self, au minimum.

La méthode magique __init__() est appelée AUTOMATIQUEMENT APRÈS la création de l'objet.

Définition d'une Classe en Python⚓︎

Exemple de Classe⚓︎

class Personne:
  def __init__(self, prenom, nom="DUPOND", ville="Marseille"):
    self.age = 33
    self.__prenom = prenom
    self._nom = nom
    self.ville = ville
  def saluer(self):
    print("Salut",self.__prenom)
  def __str__(self):
    return "Nom: {} {}, Âge: {}, Habite à {}".format(self.__prenom,self._nom,self.age,self.ville)

Instancier la Classe⚓︎

Nous sauvegardons le code précédent définissant la Classe Personne, dans le fichier classPersonne.py.

Voici quelques exemples d'instanciation de la Classe Personne, à ajouter après la définition de la classe Personne :

moi = Personne("Laurent")
julie = Personne("Julie")
print(moi.age)
print(moi)
julie.saluer()
print(julie._nom)

Exécuter ce fichier comme un script⚓︎

On peut ensuite exécuter ce fichier comme un script de la manière usuelle:

$ cd (chemin ou se trouve votre fichier)
$ python classPersonne.py
33
Nom: Laurent DUPOND, Âge: 33, Habite à Marseille
Salut Julie
DUPOND

Utiliser/Importer ce fichier comme un module⚓︎

Comprendre que nous avons d'abord un problème à résoudre⚓︎

Comme tout fichier en .py, ce fichier classPersonne.py peut être vu comme un module Python.

On pourrait donc souhaiter :

  • Importer le module classPersonne ainsi défini dans un autre fichier Python, pour pouvoir récupérer/utiliser toutes les fonctionnalités de la Classe Personne, mais dans un autre fichier .py. Remarquons au passage que cette manière de travailler est bien meilleure, car modulaire donc plus lisible, et plus facilement maintenable.
  • Importer le module classPersonne dans un Terminal (Interactif) Python, afin de faire du débuggage, et voir le résultat produit instantanément en ligne de commandes

Dans chacun de ces deux cas, on s'aperçoit que les lignes \(1\) à \(6\), que l'on avait placées après la définition de la Classe Personne pour faire des tests d'instanciation, vont être également exécutées. Or, s'il semble intéressant de pouvoir exécuter ces lignes lors de l'éxécution du fichier comme un script, il semble également intéressant que ces lignes ne soient plus exécutées :

  • lors de l'import de ce module dans un autre fichier, car dans ce cas ill semble légitime de penser que l'on n'est plus en mode débuggage
  • lors de l'import de ce module dans un Terminal interactif Python, car dans ce cas, on souhaite probablement plutôt interagir avec la Classe Personne en ligne de commande

On souhaiterait pourtant conserver ces lignes lorsque le fichier est exécuté en mode script.

Comment résoudre ce problème?⚓︎

Tous les modules sont des Objets en Python, donc ils disposent d'attributs et de méthodes. Pour résoudre ce problème qui peut sembler à priori insoluble, Python utilise l'attribut __name__ du module classPersonne

 if __name__ == "__main__":
   # Ces lignes sont exécutées si le fichier est exécuté comme un script
   # mais PAS si importé comme un module dans un autre fichier .py  ( /par un autre module), 
   # NI si importé comme module depuis un Terminal

Cela résout donc parfaitement notre problème!

Votre module classPersonne (donc le fichier classPersonne.py) doit donc maintenant contenir :

class Personne:
  def __init__(self, prenom, nom="DUPOND", ville="Marseille"):
    self.age = 33
    self.__prenom = prenom
    self._nom = nom
    self.ville = ville
  def saluer(self):
    print("Salut",self.__prenom)
  def __str__(self):
    return "Nom: {} {}, Âge: {}, Habite à {}".format(self.__prenom,self._nom,self.age,self.ville)
if __name__ == "__main__":
  moi = Personne("Laurent")
  julie = Personne("Julie")
  print(moi.age)
  print(moi)
  julie.saluer()
  print(julie._nom)
Hein ? Quoi? Pourquoi? Qu'est-ce que ça veut dire le __name__? et le __main__ ?⚓︎

En théorie, la variable __name__, qui est donc un attribut existant pour tout module Python, contient le nom du module sous forme d'une chaîne de caractère (str). En pratique néanmoins, __name__ prend des valeurs particulières et différentes selon le module dans lequel il est exécuté, ainsi :

  • __name__ = "__main__", dans l'un des trois cas suivants :
  • lorsque le module (c'est-à-dire le fichier .py) est lu à partir de l'entrée standard,
  • lorsqu'il est exécuté comme un script,
  • lorsqu'il est lancé à partir d'une invite interactive.
  • __name__ = "nom_du_module_importé" (c'est-à-dire en pratique, le nom du fichier .py importé comme un module) lorsque celui-ci est importé comme module :
  • depuis un autre module (c'est-à-dire depuis un autre fichier .py), ou bien,
  • depuis un Terminal

Cette condition if __name__ == "__main__": est donc ajoutée afin que le code qui lui succède : * Soit exécuté s'il est lancé comme un script * MAIS PAS s'il est importé comme un module (ni depuis un autre fichier, ni depuis un Terminal interactif)

Exemple d'Utilisation/Importation de la Classe Personne depuis un Terminal interactif⚓︎

On importe la classe Personne du module classPersonne, en tapant dans un Terminal :

# le répertoire courant DOIT être celui où se trouve votre fichier `classPersonne.py`
$ ipython   # pour entrer dans le Terminal interactif
Python 3.8.6 (default, Sep 30 2020, 04:00:38) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from classPersonne import Personne

La syntaxe générale est donc:

In [1]: from nomDuModule import NomDeLaClasse

On peut alors utiliser la classe Personne depuis le Terminal interactif, et accéder à tous ses attributs et toutes ses méthodes

In [1]: from classPersonne import Personne
In [2]: paul=Personne("Paul")
In [3]: print(paul)
Nom: Paul DUPOND, Âge: 33, Habite à Marseille
In [4]: paul.age
Out[4]: 33
In [5]: paul._nom
Out[5]: 'DUPOND'
In [6]: paul.__prenom
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-6e451405cdf3> in <module>
----> 1 paul.__prenom

AttributeError: 'Personne' object has no attribute '__prenom'

Et OUI! C'est normal, rappelons-nous que __prenom est un attribut privé, et nous sommes en train de tenter d'accéder à l'attribut __prenom (depuis le Terminal interactif, donc) depuis l'extérieur de la Classe...

Exemple d'Utilisation/Importation de la Classe Personne depuis autre module⚓︎

Dans le dossier courant, celui qui contient votre fichier/module classPersonne.py, créer un nouveau fichier nommé test.py, et entrer les instructions suivantes:

# ceci est le fichier test.py (i.e. module test)
from classPersonne import Personne
laura = Personne("Laura")
laura._nom = "DURAND"
laura.age = 19
laura.ville = "Paris"
laura.saluer()
print(laura.age)
print(laura.ville)
print(laura)