Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Programmation objet en Python

En Python tout est objet.
De même que Monsieur Jourdain faisait de la prose sans le savoir, vous faites avec Python, de la POO (Programmation Orientée Objet) sans vous en rendre compte ; vous avez manipulé dès le départ des instances de classe, des méthodes de classes.

Classes et méthodes de classe

Regardez attentivement cette suite de commandes :

>>> maliste = [5,4,3,2,1]
>>> type(maliste)
class 'list'
>>> len(maliste)
5
>>> maliste.count(5) 		# compter les occurrences 5 dans la liste
1
>>> maliste.index(2) 		# position de l'occurence 2 dans la liste
3

Quand vous écrivez type(maliste) l'interface retourne le message : class 'list'
La variable "maliste" est en effet un objet de type "list" ; on dit aussi une instance de la classe "list".
Ensuite vous manipulez cet objet 'list' et alors vous rencontrez deux syntaxes différentes :

Len() est une fonction générique qui peut s'appliquer à des objets de types différents (appartenant à différentes classes).
Vous connaissez d'autres fonctions natives : abs(), dir(), round(), print(), type(), range(), input(), range(), etc.
Par contre count() & index() sont des fonctions spécifiques la classe 'list' ; on parle plus précisément de méthodes de classe.
Pour appliquer une méthode de classe à un objet de ladite classe, il faut utiliser la notation pointée : instanceDeClasse.méthodedeClasse(arguments éventuels).

La terminologie et syntaxe de la POO

Nous abordons dans ce chapitre un gros morceau qui peut faire peur : la POO (Programmation Orientée Objet).
Rassurez vous, comme vous êtes déjà familiarisé avec les classes, les instances de classe et les méthodes de classe, ça ne devrait pas poser pour vous de grosses difficultés.

Prenons un exemple tiré de la vie courante pour illustrer la POO.
Médor est un chien. Pendant une journée typique, il fait diverses actions: il mange, il court, il dort, etc.

La syntaxe

Terminologie

En POO on utilise les termes suivants : classe, instance de classe (ou objet), attributs et méthodes de classe.

La relation entre un attribut OU une méthode et l’objet est indiquée par un point écrit entre les deux. C'est ce qu'on appelle la notation pointée.

Des programmes orientés objet

La POO correspond à une autre manière d’imaginer, de construire et d’organiser son code.
Python est résolument orienté objet, tout est construit autour de la notion d'objet.

Premier programme en POO

Le code

# nom programme :  poo1.py
# création classe Personne avec deux attributs 
class Personne:     
   def __init__(self,n,a):         
       self.nom = n       
       self.age = a
# création d'une instance dans la classe Personne
p1 = Personne("Albert",27) 
print(f"nom de p1 : {p1.nom} et âge de p1 {p1.age}")

# instanciation de Personne
p2 =Personne("Evelyne",47)
print(f"nom de p2 :  {p2.nom} et âge de p2 :  {p2.age}")
# modifier les attributs de objet p1
p1.age = 28
p1.nom ="Einstein"
print(f"nom et âge modifiés de p1 {p1.nom}  {p1.age}")

print(type(p1))
print(type(p2))

Notez que j'ai systématiquement utilisé des chaines formatées ("f string") pour les affichages.

Le rendu

nom de p1 : Albert et âge de p1 27
nom de p2 :  Evelyne et âge de p2 :  47
nom et âge modifiés de p1 Einstein  28
class '__main__.Personne'>
class '__main__.Personne'>

Ce programme crée une classe nommée "Personne" puis :

  1. il définit pour cette classe deux attributs : nom & age
  2. ensuite il crée une instance de la classe Personne nommé "p1"
  3. puis il crée un deuxième objet dans cette classe nommé "p2"
  4. enfin il modifie les attributs de l'objet "p1"
  5. il affiche les propriétés des instances et le type de ces instances.

Deuxième programme

Le précédent programme ne définissait pour la nouvelle classe que des attributs (ou propriétés). Dans le programme qui suit on définit non seulement des attributs mais aussi des méthodes.

Le code

#nom du programme : poo2.py
class Rectangle:     
    def __init__(self,L,l):         
        self.Longueur=L         
        self.Largeur=l
     # méthodes de classe  ci-dessous 
    def surface(self):         
        return self.Longueur*self.Largeur
    def perimetre(self):         
        return (self.Longueur* 2) + (self.Largeur*2)
# création d'une instance de Rectangle
rect1 = Rectangle(7,5)
print("Caractéristiques de rect1" , end=" : ")
print(f"la surface est :  \
	{rect1.surface()} et le périmètre est : {rect1.perimetre()}")

# modifier les attributs de l'instance r1
rect1.Longueur = 9
rect1.largeur = 6
print (f"Nouveaux périmètre et surface de rect1 : \
	{rect1.surface()} et  {rect1.perimetre()}")

#création d'un objet Rectangle
rect2 = Rectangle(6,6)
print("Caractéristiques de rect2" , end=" : ")
print(f"la surface est : {rect2.surface()} et \
	le périmètre est : {rect2.perimetre()}")

Dans ce programme je vous montre que dans le cadre de chaines formatées je peux mettre entre accolades des expressions complexes telles rect1.surface(), rect1.perimetre().

Les méthodes de classe sont des fonctions donc les parenthèses obligateoires.

Le rendu

Caractéristiques de rect1 : la surface est : 35 et le périmètre est : 24
Nouveaux périmètre et surface de rect1 : 45 et  28
Caractéristiques de rect2 : la surface est : 36 et le périmètre est : 24

Ce programme crée une classe nommée "Rectangle" et définit pour cette classe :

    Ensuite :
  1. il crée une instance de cette classe nommé "rect1"
  2. il calcule la surface et le périmètre de l'objet rect1 en utilisant les méthodes de classe
  3. il modifie les attributs de rect1
  4. il crée un deuxième objet de type Rectangle : rect2
  5. il calcule et affiche la surface et périmètre de rect2

Syntaxe Python et POO

Maintenant que vous avez observé attentivement le code de ces deux programmes on peut présenter la syntaxe Python dans le cadre de la POO

Définir une classe

Syntaxe : class Nomclasse:
Par convention le nom d’une classe commence toujours par une majuscule.
Dans le même bloc il faut définir les attributs et méthodes de la classe.

Définir les attributs de classe

 def __init__(self,param1, param2, ...):         
        self.variable1=param1       
        self.variable2=param2
		...

Le mot "self" est obligatoire.
__init__() est une méthode spéciale (ou méthode magique ou méthode à double underscore).
Nous y reviendrons en fin de chapitre.

Définir une méthode de classe

def méthode(self):         
        return expression 

Il faut au minimum le paramètre "self". Le mot "self" sera expliqué plus loin.
L'expression est construite à partir des attributs de classe.

Créer une instance de classe

Syntaxe : nomObjet = NomClasse(propriété1, propriété2, etc. )
C'est comme l'appel d'une fonction !

Modifier un attribut d'un objet

Syntaxe : nomObjet.attribut = nouvelle valeur

Appliquer une méthode à un objet

Syntaxe : nomObjet.méthode()

Le mot self

Il faut expliquer ce que signifie ce mot "self" qui est omniprésent dans la syntaxe.
Une des particularités des méthodes est que l’objet qui l’appelle est passé comme premier argument de la fonction telle que définie dans la classe. Ainsi, lorsqu’on écrit rect1.surface() par exemple, l’objet "rect1" est passé de manière implicite à "surface()".
Aussi une méthode possède toujours à minima le paramètre "self" et lorsqu'on appelle ladite méthode, il n'y a aucun argument à saisir.
Si la méthode __init__() comprend trois paramètres (dont self), il faut alors passer deux arguments pour créer une nouvelle instance de classe.

Héritage

En POO on dit qu’un objet “hérite” des méthodes de sa classe ; cela signifie qu'une instance de classe a accès aux méthodes de cette classe.
Ainsi une instance de la classe Rectangle a accès aux méthodes surface() & perimetre().

Une classe peut "hériter" d'une autre classe : une classe dite "fille" hérite de toutes les attributs et méthodes de la classe parent.

Premier programme avec héritage

Le code

# nom programme : heritage.py
#création d'une classe Personne avec deux attributs
class Personne:     
     def __init__(self,nom,age):         
         self.nom = nom         
         self.age=age
class Etudiant(Personne): 
# La classe  Etudiant hérite de la classe Personne
    # définition des attributs
    def __init__(self,nom,age,filiere):
        # héritage des attributs depuis la classe mère Personne        
        Personne.__init__(self,nom,age)         

        # ajout d'un nouvel attribut filiere à la classe Etudiant
        self.filiere = filiere
   
etudiant1 = Etudiant("Albert",27,"math") 
print("nom, âge, filière : " ,etudiant1.nom,  \
	" ", etudiant1.age, " " ,etudiant1.filiere)
print(type(etudiant1))

#modifier les attributs d'un objet
etudiant1.nom ="Alberta"
etudiant1.age = 28

#créer d'une nouvelle instance de la classe Etudiant
etudiant2 =Etudiant("Isabelle",21,"informatique")
print("nom, âge, filière : " ,etudiant2.nom, \
	" ", etudiant2.age, " " ,etudiant2.filiere)
print(type(etudiant2))

Que fait ce programme ?

Le rendu

nom, âge, filière :  Albert   27   math
class '__main__.Etudiant'>
nom, âge, filière :  Isabelle   21   informatique
class '__main__.Etudiant'>

Deuxième programme

Le code

# nom programme : heritage2.py
#création d'une classe Personne
class Personne:     
     def __init__(self,nom,age):         
         self.nom = nom         
         self.age=age
# La classe fille Etudiant hérite de la classe Personne
class Etudiant(Personne): 
    # définition des attributs
        def __init__(self,nom,age,filiere):
        # héritage des attributs depuis la classe mère Personne        
                Personne.__init__(self,nom,age)         
        # ajout d'un  troisième attribut
                self.filiere = filiere
        #ajout d'une méthode
        def  affiche(self):
          return f"nom : {self.nom} âge : {self.age} filière : {self.filiere}"

#créer une instance de Etudiant      
etudiant1 = Etudiant("Albert",27,"math") 
print("Nom, âge, filière : " ,etudiant1.nom \
	, " ", etudiant1.age, " " ,etudiant1.filiere)

#modifier les attributs d'un objet
etudiant1.nom ="Alberta"
etudiant1.age = 28

#créer d'une nouvelle instance de Etudiant
etudiant2 =Etudiant("Isabelle",21,"informatique")
print("Nom, âge, filière : " ,etudiant2.nom \
	, " ", etudiant2.age, " " ,etudiant2.filiere)

#utiliser la méthode de classe pour simplifier les affichages
print(etudiant1.affiche())
print(etudiant2.affiche())

Que fait ce code de plus par rapport au précédent ?

Il définit dans la classe Etudiant une méthode affiche() qui produit la fiche de l'étudiant.
Notez l'instruction : return f"nom : {self.nom} âge : {self.age} filière : {self.filiere}". La méthode retourne une chaine formatée.
Si un des attributs était la date de naissance on pourrait imaginer une méthode qui calcule l'âge de l'étudiant.

Le rendu

Le Shell de l'IDLE affiche :

Nom, âge, filière :  Albert   27   math
Nom, âge, filière :  Isabelle   21   informatique
nom : Alberta âge : 28 filière : math
nom : Isabelle âge : 21 filière : informatique

l'instruction print(etudiant1.affiche()) retourne la même info que
print("Nom, âge, filière : " ,etudiant1.nom, " ", etudiant1.age, " " ,etudiant1.filiere)
Admettez que la méthode de classe affiche() est pratique !

Les méthodes magiques ou spéciales

Dans le cadre de la POO on peut utiliser les méthodes spéciales dites aussi méthodes magiques.
Ellles sont appelées parfois méthodes "double underscore" car elles sont précédées et suivies toujours par deux undersocres.
La plus connue des méthodes magiques est __init()__ ; elle est incontournable et est utilisée pour créer une classe d'objets.

Programme avec une méthode magique

Nous allons employer la méthode magique __str__()

Le code

# nom du programme : methodes_speciales.py
# test des méthodes magiques les plus connues : init, str
class Personne:
    def __init__(self, nom, email):
        self.nom = nom
        self.email = email

    def __str__(self):
        return f"Personne( {self.nom} a pour courriel : {self.email})"

p1 = Personne("Martin" , "martin@free.fr")
print(p1)
p2 =Personne("Louis", "louis@gmail.com")
print(p2)

print("------------------------------")
p1.nom ="Martins"
print(p1)
p2.email ="louis@hotmail.fr"
print(p2)

La méthode magique __str__() est utilisée pour créer une chaine formatée. Ce format sera automatiquement (comme par magie) utilisé avec l'instruction print(instanceDeClasse).

Le rendu

Personne( Martin a pour courriel : martin@free.fr)
Personne( Louis a pour courriel : louis@gmail.com)
------------------------------
Personne( Martins a pour courriel : martin@free.fr)
Personne( Louis a pour courriel : louis@hotmail.fr)

Donc pour afficher la fiche d'un étudiant il suffit de taper print(nomInstance)
C'est donc encore plus simple qu'avec la méthode de classe (print(nomInstance.methode())

Il faut appeler de façon explicite les méthodes de classe alors que la méthode magique __str()__ est appelée automatiquement via la fonction print().

Les autres méthodes magiques

POO et jeux vidéos

La POO est particulièrement utile pour la réalisation de jeux vidéos en Python avec la bibliothèque Pygame.
En effet pour certains jeux il faut créer et manipuler plusieurs dizaines de sprites. En créant des classes de sprites le développement de tels jeux reste simple d'autant que toutes les classes que vous allez définir hériteront de la classe pygame.sprite.Sprite et donc de ses multiples méthodes.
Pygame & POO - jeu du casse-briques

Dans le chapitre en lien je vous montre comment réaliser deux jeux vidéos (dont le casse-briques) simplement en créant et manipulant des classes.