Aller au contenu

Écriture de notre classe en POO

Notion sur la POO en Python#

Le framework Processing utilise le concept de la Programmation Orientée Objet. Il existe un tutoriel sur le site d'OpenClassRooms sur le sujet.

Mais depuis le début de la formation, nous l'utilisons sans trop le savoir. Les objets Qgs*, comme QgsVectorLayer utilisent le principe de la POO.

On a pu créer des objets QgsVectorLayer en appelant son constructeur :

1
2
3
from qgis.core import QgsVectorLayer

layer = QgsVectorLayer("C:/chemin/vers/un/fichier.gpkg|layername=communes", "communes", "ogr")

et ensuite, on a pu appeler des méthodes sur cet objet, comme :

1
2
layer.setName("Communes")
layer.name()  # Retourne "Communes"

Tip

Vous pouvez relire le passage sur la POO en début de formation.

Exemple#

Nous allons faire un "très" petit exemple rapide. Écrivons notre premier jeu vidéo en console ! 🎮

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from time import sleep

MAX_ENERGIE = 20


class Personnage:

    """ Classe représentant un personnage du jeu vidéo. """

    def __init__(self, un_nom, energie=MAX_ENERGIE):
        """ Constructeur. """
        self.nom = un_nom
        self.energie = energie

    def marcher(self):
        """ Permet au personnage de marcher.

        Cela dépense de l'énergie.
        """
        cout = 5
        if self.energie >= cout:
            print(f"{self.nom} marche.")
            self.energie -= cout
        else:
            print(f"{self.nom} ne peut pas marcher car il n'a pas assez d'énergie.")

    def courir(self):
        """ Permet au personnage de courir.

        Cela dépense de l'énergie.
        """
        cout = 10
        if self.energie >= cout:
            print(f"{self.nom} court.")
            self.energie -= cout
        else:
            print(f"{self.nom} ne peut pas courir car il n\'a pas assez d\'énergie.")

    def dormir(self):
        """ Permet au personnage de dormir et restaurer le niveau maximum d'énergie."""
        print(f"{self.nom} dort et fait le plein d'énergie.")
        for i in range(2):
            print('...')
            sleep(1)
        self.energie = MAX_ENERGIE

    def manger(self):
        """ Permet au personnage de manger et d'augmenter de 10 points le niveau d'énergie."""
        energie = 10
        print(f"{self.nom} mange et récupère {energie} points d'énergie.")
        if self.energie <= MAX_ENERGIE - energie:
            self.energie += energie
        else:
            self.energie = MAX_ENERGIE

    def __repr__(self):
        return f"<Personnage: '{self.nom}' avec {self.energie} points d'énergie>"

Utilisation de notre classe#

  • dir(a) est une fonction indique les "membres" de notre variable Personnage.
  • help(a) affiche la documentation de notre classe
  • __dict__ est une propriété qui donne les valeurs des attributs de l'instance.
1
2
3
a = Personnage('Dark Vador')
dir(a)
help(a)

Que remarquons-nous ?

Solution
1
2
3
4
5
a = Personnage('Dark Vador')
a.courir()
a.dormir()
a.manger()
print(a)

Afficher le nom du personnage (et juste son nom, pas la phrase de présentation)

Ajouter d'autres méthodes#

Ajoutons une méthode dialoguer pour discuter avec un autre personnage.

Tip

1
2
3
def dialoguer(self, autre_personnage):
    """ Permet de dialoguer avec un autre personnage. """
    pass
  1. Écrire le code la fonction à l'aide d'un print pour commencer disant que X dialogue avec Y.
  2. Vérifier le niveau d'énergie avant de dialoguer ! Difficile de discuter si on n'a plus d'énergie 😉
  3. Garder son code à gauche, on peut utiliser une instruction return

Nous pouvons désormais utiliser le constructeur afin de créer deux instances de notre classe.

1
2
b = Personnage('Luke')
b.dialoguer(a)
Solution pour la méthode dialoguer()
1
2
3
4
5
6
def dialoguer(self, autre_personnage):
    if self.energie <= 0:
        print(f"{self.nom} ne peut pas dialoguer car il n'a pas assez d'énergie.")
        return

    print(f"{self.nom} dialogue avec {autre_personnage.nom} et ils échangent des informations secrètes")

Continuons notre classe pour la gestion de son inventaire. Admettons que notre personnage puisse ramasser des objets afin de les mettre dans son sac à dos.

  1. Il va falloir ajouter une nouvelle propriété à notre classe de type list que l'on peut nommer inventaire. Par défaut, son inventaire sera vide.
  2. Ajoutons 3 méthodes : ramasser, deposer et utiliser. Pour le moment, pour faciliter l'exercice, utilisons une chaîne de caractère pour désigner l'objet. Ces méthodes vont interagir avec notre inventaire à l'aide des méthodes remove(), append() que l'on trouve sur une liste.
  3. Pour les méthodes deposer et utiliser, nous pouvons avoir à créer une autre méthode privée afin de vérifier l'existence de l'objet dans l'inventaire. Par convention, nous préfixons la méthode par _ comme _est_dans_inventaire afin de signaler que c'est une méthode dite privée. L'utilisation de cette méthode privée est uniquement à titre pédagogique, on peut vouloir exposer la méthode est_dans_inventaire. Cette méthode doit renvoyer un booléen.
  4. Ajoutons des commentaires et/ou des docstrings, CF mémo Python. On peut utiliser la méthode help.
  5. Pensons aussi annotations Python

  6. Refaire la commande help(a) pour voir le résultat final 😉

Info

Il est important de comprendre que la POO permet de construire une sorte de boîte opaque du point de vue de l'utilisateur de la classe. Un peu comme une voiture, elles ont toutes un capot et une pédale d'accélération. L'appui sur l'accélérateur déclenche plusieurs mécanismes à l'intérieur de la voiture, mais du point de vue utilisateur, c'est plutôt simple.

Tip

On peut vite imaginer d'autres classes, comme Arme, car ramasser un bout de bois ou un sabre laser n'a pas le même impact lors de son utilisation dans un combat. Le dégât qu'inflige une arme sur le niveau d'énergie de l'autre personnage est une propriété de l'arme en question et du niveau du personnage.

Des idées pour continuer plus loin#

Des jeux en Python dans QGIS :

Ou pour le fun avec des expressions :

Solution#

Sur la classe Personnage ci-dessus :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def _est_dans_inventaire(self, un_objet: str) -> bool:
    """ Fonction "interne" pour tester si un objet est dans l'inventaire. """
    # On ne souhaite pas qu'un autre personnage puis vérifier le contenu d'un inventaire d'un autre.
    # Il faut plutôt lui demander :)
    # Note cette méthode n'est pas dans le help()
    return un_objet in self.inventaire

def ramasser(self, un_objet: str) -> bool:
    """ Ramasser un objet et le mettre dans l'inventaire.

    Retourne True si l'action est OK
    """
    print(f"{self.nom} ramasse {un_objet} et le met dans son inventaire.")
    self.inventaire.append(un_objet)
    return True

def utiliser(self, un_objet: str) -> bool:
    """ Utiliser un objet s'il est disponible avant dans l'inventaire.

    Retourne True si l'action est OK
    """
    if self._est_dans_inventaire(un_objet):
        print(f"{self.nom} utilise {un_objet}")
        return True

    print(f"{self.nom} ne possède pas {un_objet}")
    return False

def deposer(self, un_objet: str) -> bool:
    """ Retirer un objet de l'inventaire.

    Retourne True si l'action est OK
    """
    if not self._est_dans_inventaire(un_objet):
        return False

    print(f"{self.nom} dépose {un_objet}")
    self.inventaire.remove(un_objet)
    return True

def donner(self, autre_personnage, un_objet: str) -> bool:
    """ Donner un objet à un autre personnage.

    Retourne True si l'action est OK
    """
    if not self._est_dans_inventaire(un_objet):
        return False
    self.inventaire.remove(un_objet)
    autre_personnage.inventaire.append(un_objet)
    print(f"{autre_personnage.nom} reçoit {un_objet} de la part de {self.nom} et le remercie 👍")
    return True