Plan du site  
français  English
pixel
pixel

Articles - Étudiants SUPINFO

Déclarer une classe

Après avoir rappelé quelques uns des concepts définis dans le module précédent, on va apprendre à les implémenter en Python.

[Note]

Même si Python est le langage choisi pour cette introduction à l'orienté objet, les autres langages fonctionnent plus ou moins de la même façon sur ce plan.

Rappels de quelques définitions

Un objet est une entité possédant :

  1. Une identité.

  2. Des variables définissant son état appelées attributs.

  3. Des sous programmes gérant son comportement appelés méthodes.

Une classe est une abstraction regroupant des objets ayant les mêmes attributs et les mêmes méthodes.

En Python, et dans les autres langages, on va commencer par déclarer une classe qui sera une sorte de moule commun à nos futurs objets. Ensuite, selon nos besoins, on instanciera des objets de cette classe, chacun d'eux ayant une identité et des valeurs d’attributs qui lui sont propres.

Syntaxe générale

On déclare une classe en Python à l'aide du mot clé class :

class nomDeLaClasse:
    """documentation de la classe"""
    ...
[Note]

On va bien sûr préciser tout cela dans la suite de ce chapitre.

[Note]

Écrire une documentation pertinente fait partie des bonnes pratiques. On peut accéder à la documentation d’une classe par la commande :

nomDeLaClasse.__doc__

Cette commande retourne une chaîne de caractère avec laquelle on pourra faire par exemple une affectation ou un affichage.

Pour créer un objet d’une classe existante, on utilise la syntaxe :

monObjet = nomDeLaClasse()

Example 1.1. Déclaration d'une classe "compte"

On déclare ici une classe permettant (à terme) de gérer un compte bancaire, et on instancie ensuite un objet :

class compte:
    """gestion d'un compte bancaire"""

monCompte = compte()
print(monCompte)
print(monCompte.__doc__)
<__main__.compte object at 0x1262af0>
gestion d'un compte bancaire

On va voir dans la suite comment définir les attributs et les méthodes d’une classe, mais retenons déjà comment on pourra les appeler :

monObjet.monAttribut

monObjet.maMethode(para1,...)

On fait donc précéder le nom de l'attribut ou de la méthode du nom de l'objet et d'un point. Cette façon de faire sera commune à beaucoup de langage orientés objets.

[Note]

Comme pour les variables ou les fonctions en programmtion procédurale, on prendra l’habitude de nommer les attributs et méthodes en "lowerCamelCase".

Ajout dynamique d'attributs

Après avoir instancié un objet on peut lui ajouter dynamiquement des attributs en suivant une syntaxe de la forme :

monObjet.monAttribut = valeur

Example 1.2. Ajout d'attributs à la classe "compte"

Dynamiquement, on ajoute des attributs "nom", "numéro" et "solde" à la classe compte :

class compte:
    """gestion d'un compte bancaire"""

monCompte = compte()
monCompte.nom = "comptePerso"
monCompte.numero = 666
monCompte.solde = -1000

[Note]

Cette façon de déclarer des attributs n’est pas bien sûr pas satisfaisante car elle viole complètement le principe d'encapsulation. On verra dans la suite que la déclaration des attributs devra se faire dans une méthode spéciale appelée constructeur.

Procéder ainsi nous permet néanmoins d’effectuer nos premières manipulations d'attributs.

Déclaration des méthodes

Lors de la déclaration d’une classe, on doit déclarer ses méthodes :

class nomDeLaClasse:
    """documentation de la classe"""
    def methode1(self,para1,para2,...):
        ...
    def methode2(self,para1,para2,...):
        ...

Quelques points importants à retenir :

  • Ces déclarations de méthodes se font nécessairement dans la classe.

  • En Python la syntaxe des méthodes est bien sûr la même que celle des fonctions.

  • Le premier paramètre d’une méthode est toujours une référence vers l’objet courant. Elle est usuellement appelée “self”.

  • Les autres paramètres sont les données transmises à la méthode pour son bon fonctionnement.

Lors de l'implémentation d'une méthode, pour accéder à un des attributs on fera précéder son nom de self et d'un point.

Example 1.3. Ajout de méthodes à la classe "compte"

On ajoute ici deux méthodes à la classe "compte" :

class compte:
    """gestion d'un compte bancaire"""
    def crediter(self,x):
        self.solde += x
    def afficherSolde(self):
        print("Le solde vaut :",self.solde)

monCompte = compte()
monCompte.nom = "comptePerso"
monCompte.numero = 6666
monCompte.solde = -1000
monCompte.crediter(5000)
monCompte.afficherSolde()
Le solde vaut : 4000

Attributs et méthodes de classe

Les attributs précédents sont aussi appelés attributs d’instances. Cela signifie que leur valeur peut être différente pour chaque objet.

On peut également définir des attributs de classes. Ceux-ci auront la même valeur pour tous les objets de la classe.

La déclaration d'un attribut de classe doit s'effectuer au sein même de la classe. En voici la syntaxe générale :

class nomDeLaClasse:
    """documentation de la classe"""
    monAttributDeClasse = valeur
    def methode1(self,para1,para2,...):
        ...

L'accès à un attribut de classe se fera alors comme suit :

maClasse.monAttributDeClasse

Example 1.4. Ajout à la classe "compte" d'un attribut de classe

Ici on ajoute un attribut de classe afin de décompter le nombre d'objets instanciés de la classe "compte". Il est naturellement initialisé à 0 :

class compte:
    """gestion d'un compte bancaire"""
    nbreCompte = 0
    def crediter(self,x):
        self.solde += x
    def afficherSolde(self):
        print("Le solde vaut :",self.solde)

print(compte.nbreCompte)
monCompte = compte()
monCompte.nom = "comptePerso"
monCompte.numero = 666
monCompte.solde = -1000
compte.nbreCompte += 1
print(compte.nbreCompte)
0
1

[Note]

Cet exemple du décompte du nombre d’objet instanciés est l’exemple typique d’attribut de classe.

Un autre rôle usuel est de stocker la valeur de certaines constantes propres à la classe (valeur de taux, etc.).

En suivant le même principe, on peut imaginer des méthodes rattachées à la classe. Celles-ci auront un effet indépendant des objets. Leur rôle sera en particulier de manipuler les attributs de classes. On les appelle bien sûr des méthodes de classe.

La déclaration d'une méthode de classe doit s'effectuer au sein de la classe. Elle requiert l’utilisation du mot clé “@classmethod” et de la classe en paramètre (usage du mot clé cls) :

class nomDeLaClasse:
    """documentation de la classe"""
    @classmethod
    def maMethodeDeClasse(cls,para1,...):
        ...

L'accès à une méthode de classe s'effectuera alors en utilisant une des deux syntaxes suivantes :

maClasse.maMethodeDeClasse(...)

unObjet.maMethodeDeClasse(...)

Lors de l'implémentation d'une méthode de classe, pour accéder à un des attributs de classe on fera précéder son nom de cls et d'un point.

Example 1.5. Ajout à la classe "compte" d'une méthode de classe

Afin de manipuler l'attribut de classe "nbreCompte", on rajoute une méthode de classe en respectant bien la syntaxe précédemment décrite :

class compte:
    """gestion d'un compte bancaire"""
    nbreCompte = 0
    @classmethod
    def afficherNbreComptes(cls):
        print("Nombre de comptes : ",cls.nbreCompte)

monCompte = compte()
compte.nbreCompte += 1
compte.afficherNbreComptes()
monAutreCompte = compte()
compte.nbreCompte += 1
monAutreCompte.afficherNbreComptes()
Nombre de comptes : 1
Nombre de comptes : 2

Constructeur et destructeur

Le but de cette partie est de présenter deux méthodes bien spécifiques qui nous permettront de gérer plus proprement nos attributs que dynamiquement. Il s'agit du constructeur et du destructeur d'une classe.

[Note]

On retrouvera ces méthodes (sous différents noms) dans la plupart des langages orientés objet.

La méthode "__init__"

Pour déterminer et initialiser les attributs d’un objet que l’on crée, on va rajouter à nos classes une méthode particulière : un constructeur.

En Python, son nom est imposé : __init__.

On prendra maintenant l’habitude de n’utiliser que cette méthode pour déterminer les attributs d’un objet et leur valeur initiale. On oubliera donc les ajouts dynamiques évoqués dans la première partie.

La syntaxe de la déclaration de la méthode __init__ est la même que celle des autres méthodes :

class nomDeLaClasse:
    """documentation de la classe"""
    def __init__(self,para1,para2,...):
        ...
    def methode1(self,para1,para2,...):
        ...

Example 1.6. Ajout d'un constructeur à la classe "compte"

On munit notre classe de trois attributs et on les initialise, le tout grâce au constructeur :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.nom = nom
        self.numero = numero
        self.solde = valeur
    def afficherSolde(self):
        print("Le solde vaut :",self.solde)

monCompte = compte("pro",777,10000)
monCompte.afficherSolde()
Le solde vaut : 10000

[Note]

La méthode “__init__” est appelée automatiquement lors de l’instanciation d’un objet. L’utilisateur de la classe n’a donc pas à l’invoquer explicitement.

Comme pour les autres méthodes en Python, on peut donner des valeurs par défaut aux paramètres de __init__. Cela permettra d'être sûr du sens de ce genre d'instanciation :

monObjet = nomDeLaClasse()

Si tous les paramètres de __init__ admettent une valeur par défaut on le qualifie également de constructeur par défaut.

Example 1.7. Le constructeur de la classe "compte" devient aussi un constructeur par défaut

On donne des valeurs par défaut à nos trois attributs :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom="perso",numero=666,valeur=1000):
        self.nom = nom
        self.numero = numero
        self.solde = valeur
    def afficherSolde(self):
        print("Le solde vaut :",self.solde)

monCompte = compte("pro",777,10000)
monCompte.afficherSolde()
monAutreCompte = compte()
monAutreCompte.afficherSolde()
Le solde vaut : 10000
Le solde vaut : 1000

Lors de la deuxième instanciation on a utilisé le constructeur par défaut, ce qui explique la valeur du solde.


Si l’on dispose d’un attribut de classe dénombrant le nombre d’objets instanciés il est pertinent de le mettre à jour dans la méthode __init__.

Example 1.8. Mise à jour de l'attribut de classe de "compte"'

Une des autres tâches de notre constructeur est l'incrémentation de l'attribut de classe "nbreCompte" :

class compte:
    nbreCompte = 0
    def __init__(self,nom="perso",numero=666,valeur=1000):
        self.nom = nom
        self.numero = numero
        self.solde = valeur
        compte.nbreCompte += 1
        
monCompte = compte("pro",777,10000)
print(compte.nbreCompte)
monAutreCompte = compte()
print(compte.nbreCompte)
1
2

La méthode "__del__"

Pour libérer la mémoire d’un objet que l’on n’utilise plus, on va rajouter à nos classes une méthode particulière : un destructeur.

En Python, son nom est imposé : __del__.

Il pourra également servir à mettre à jour certains attributs de classes, comme par exemple un compteur d’objets instanciés.

La syntaxe de la déclaration de la méthode __del__ est la même que celle des autres méthodes :

class nomDeLaClasse:
    """documentation de la classe"""
    def __init__(self,para1,para2,...):
        ...
    def __del__(self):
        ...
    def methode1(self,para1,para2,...):
        ...
[Note]

On ne fait quasiment jamais d’appel explicite à la méthode __del__. Elle est appelée automatiquement quand un objet cesse d’être référencé (comme par exemple si l’on crée une instance dans une fonction ou procédure).

[Note]

En Python c'est l'interpréteur qui gère lui même la mémoire. Le rôle d'un destructeur sera plus flagrant dans d'autres langages, en particulier le C++, où si un attribut est un pointeur il y aura nécessité de libérer la zone mémoire correspondante.

Example 1.9. Ajout d'un destructeur à la classe "compte"

Le destructeur va décrémenter l'attribut de classe "nbreCompte" :

class compte:
    nbreCompte = 0
    def __init__(self,nom="perso",numero=666,valeur=1000):
        self.nom = nom
        self.numero = numero
        self.solde = valeur
        compte.nbreCompte += 1
    def __del__(self):
        compte.nbreCompte -= 1
    @classmethod
    def afficherNbreComptes(cls):
        print("Nombre de comptes : ",cls.nbreCompte)

def unePetiteProcedure():
    unAutreCompte = compte()
    compte.afficherNbreComptes()
    
monCompte = compte()
monAutreCompte = compte()
compte.afficherNbreComptes()
unePetiteProcedure()
compte.afficherNbreComptes()
Nombre de comptes : 2
Nombre de comptes : 3
Nombre de comptes : 2

On commence par instancier deux objets de la classe “compte“. Lors de l'appel de la procédure "unePetiteProcedure“ une troisième instanciation a lieu. A ce moment là on a donc trois objets, ce que l'on constate en appelant la méthode de classe "afficherNbreComptes". Ceci dit, le dernier de ces objets a une durée de vie limitée au sous-programme l'ayant instancié. A la sortie de celui-ci, un appel implicite au destructeur a été effectué par l'interpréteur Python, et le décompte retombe donc à deux.


Encapsulation

Après avoir vu dans les parties précédentes comment déclarer une classe et instancier un objet, on va s'intéresser ici au respect du principe d'encapsulation.

Rappel du principe

L'encapsulation est le principe interdisant l’accès direct aux attributs. On ne dialogue avec l’objet qu’à travers une interface définissant les services accessibles à ses utilisateurs. C'est le rôle des méthodes.

Le principe d'encapsulation consiste donc à déclarer les attributs de façon privée et les méthodes de façon publique.

Comment rendre un attribut privé

Lors de l’implémentation de la méthode __init__ on fera précèder le nom de l’attribut en question par un double underscore :

def __init__(self,para1,para2,...):
    attributPublic = valeur
    __attributPrive = valeur
    ...
[Note]

D'autres langages seront plus explicites, avec l'utilisation d'un mot clé comme "private".

Example 1.10. L'attribut "solde" de la classe "compte" devient privé

On fait précéder le nom "solde" d'un double underscore :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.nom = nom
        self.numero = numero
        self.__solde = valeur
    def afficherSolde(self):
        print("Le solde :",self.__solde)

L'attribut "nom" est public, on peut le manipuler en dehors de la classe :

monCompte = compte("pro",777,10000)
monCompte.nom = "perso"
print(monCompte.nom)
perso

L'attribut "solde" est privé, on ne peut pas le manipuler en dehors de la classe :

monCompte = compte("pro",777,10000)
print(monCompte.__solde)
AttributeError: 'compte' object has no attribute '__solde'

Pour respecter le principe d'encapsulation, on prendra donc l’habitude de déclarer tous nos attributs de façon privée. Notre classe deviendra alors :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.__nom = nom
        self.__numero = numero
        self.__solde = valeur
    def afficherSolde(self):
        print("Le solde :",self.__solde)

[Note]

On procéderait de la même façon pour rendre une méthode privée, faire précéder son nom par un double underscore. Cela peut être utile lors de l’implémentation de certaines méthodes publiques complexes, que l’on va “découper“ en plusieurs sous-méthodes qui seront déclarées privées car étant internes à l’objet.

Idem pour rendre un attribut de classe privé.

Le rôle des accesseurs

Puisque l'on va déclarer nos attributs de façon privée, il convient d'implémenter des méthodes pour les manipuler.

Un accesseur, appelé également getter, est une méthode retournant la valeur d’un attribut. On sera donc souvent amené à créer autant d’accesseurs que d’attributs.

Example 1.11. Ajout d'un accesseur à la classe "compte"

On rajoute un getter afin de pouvoir accéder à la valeur de l'attribut "solde" :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.__nom = nom
        self.__numero = numero
        self.__solde = valeur
    def getSolde(self):
        return self.__solde

monCompte = compte("pro",777,10000)
print(monCompte.getSolde())
10000

Le rôle des mutateurs

Un mutateur, appelé également setter, est une méthode fixant la valeur d’un attribut. On sera donc souvent amené à créer autant de mutateurs que d’attributs.

Un des rôles importants d'un mutateur sera de pouvoir contrôler la façon dont on fixe la nouvelle valeur d'un attribut. Cela permet donc de conserver l'intégrité des données, elles ne pourront pas être modifiées n'importe comment.

Example 1.12. Ajout de mutateurs à la classe "compte"

On rajoute un setter afin de pouvoir modifier la valeur de l'attribut "solde" :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.__nom = nom
        self.__numero = numero
        self.__solde = valeur
    def getSolde(self):
        return self.__solde
    def setSolde(self,x):
        if x>0:
            self.__solde += x
        elif abs(x)<self.__solde:
            self.__solde += x
            

monCompte = compte("pro",777,10000)
monCompte.setSolde(-2000)
print(monCompte.getSolde())
8000

Le mutateur "setSolde" vérifie l'intégrité de l'attribut "solde" en s'assurant que la nouvelle valeur est cohérente.


Example 1.13. La classe "compte" avec des accesseurs et mutateurs pour chaque attribut

La classe est maintenant complète du point de vue du respect du principe d'encapsulation :

class compte:
    """gestion d'un compte bancaire"""
    def __init__(self,nom,numero,valeur):
        self.__nom = nom
        self.__numero = numero
        self.__solde = valeur
    def getSolde(self):
        return self.__solde
    def getNumero(self):
        return self.__numero
    def getNom(self):
        return self.__nom
    def setSolde(self,x):
        if x>0:
            self.__solde += x
        elif x<self.__solde:
            self.__solde -= x
    def setNom(self,nom):
        self.__nom = nom
    def setNumero(self,num):
        self.__numero = num

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels