POO, documentation et tests - TP4

Classes, objets, attributs et méthodes en Python
Author

Ludovic Deneuville

Introduction

Créer l’arborscence

Vous pouvez créer chaque dossier et fichier à la main, ou utiliser dans un terminal Git Bash les commandes ci-dessous (mkdir : make directory):

# Sur la VM ensai
mkdir -p /p/Cours1A/IPOO/TP4/Exercice_{1..3}/

# Sur un Datalab Onyxia
mkdir -p /home/onyxia/work/IPOO/TP4/Exercice_{1..3}/

Moodle

1 Cours

1.1 Classes et Objets

  • une classe est comme un “moule” qui sert à fabriquer des objets
  • un objet est une instance de classe
    • il est créé via un appel au constructeur
  • une classe est composée
    • d’attributs : ce qu’elle est
    • de méthodes : ce qu’elle peut faire
Important
  • un nom de classe s’écrit en CamelCase (majuscule à chaque mot)
  • une seule classe par module
    • (module : fichier, package : dossier)
    • le nom du module s’écrit en snake_case (mots en minuscules séparés par des underscores : _)

1.2 Exemple de classe

Définir la classe Frac

frac.py
class Frac:
    def __init__(self, num, denom=1) -> None:
        """Constructeur"""
        self.num = num 
        self.denom = denom

    def add(self, autre_fraction) -> "Frac":
        """Additionne deux fractions"""
        numerateur = self.num * autre_fraction.denom + autre_fraction.num * self.denom 
        denominateur = self.denom * autre_fraction.denom 
        return Frac(numerateur, denominateur)

    def __str__(self) -> str:
        """Retourne une chaîne affichant la fraction"""
        return f"{self.num} / {self.denom}"

Utiliser la classe Frac pour créer des objets

__main__.py
from frac import Frac

f1 = Frac(1, 2)
f2 = Frac(1, 4)

f3 = f1.add(f2)
print(f3)

1.3 self

Dans la définition d’une classe, self représente l’instance actuelle :

  • self.denom : représente l’attirbut denom de l’objet
  • def add(self, autre_fraction) : dans la définition de chaque méthode, le premier paramètre sera self
  • cependant lors de l’appel de la méthode self n’apparait pas : f1.add(f2)

1.4 Méthodes

Les méthodes de classe sont des fonctions qui s’appliquent à un objet :

  • la syntaxe est la suivante : mon_objet.ma_methode()
  • à ne pas confondre avec les fonctions classiques ma_fonction(mon_objet)

Ces méthodes peuvent :

  • obtenir / modifier les valeurs des attributs de l’objet
  • retourner une valeur, un objet ou rien
Méthode Description Type de retour
__init__(self, num, den) Initialise un objet de type Fraction. None
get_numerateur(self) Retourne le numérateur de la fraction. int
get_denominateur(self) Retourne le dénominateur de la fraction. int
set_numerateur(self, num) Modifie le numérateur de la fraction. None
set_denominateur(self, den) Modifie le dénominateur de la fraction. None
__str__(self) Représentation en chaîne de caractères (ex: 3/4). str
simplifier(self) Simplifie la fraction en divisant par le PGCD. Fraction
to_decimal(self) Retourne la valeur décimale de la fraction. float
inverse(self) Retourne une nouvelle fraction inversée. Fraction
add(self, autre_fraction) Addition avec une autre fraction. Fraction

2 Templates

2.1 Classe

class MaClasse:
    """titre_ma_classe

    Description

    Attributes
    ----------
    attribut_1 : type_attribut_1 (str, int, float, list, bool, function...)
        desc_attribut_1
        
    attribut_2 : type_attribut_2
        desc_attribut_2
    """
    
    def __init__(self, parametre_1, parametre_2):
        """Constructeur de l'objet

        Parameters
        ----------
        parametre_1 : type_parametre_1
            desc_parametre_1
            
        parametre_2 : type_parametre_2
            desc_parametre_2
        """
        self.attribut_1 = parametre_1
        self.attribut_2 = parametre_2

    def ma_methode(self, param1):
        """desc_courte_ma_methode

        desc_longue_ma_methode_facultatif       

        Parameters
        ----------
        param1 : type
            desc_param_1
            
        Returns
        -------
        type
            desc_retour

        Examples
        --------
        >>> mon_obj = ma_classe("toto")
        >>> mon_obj.ma_methode("xxx")
        2
        """
        return 2

    def __str__(self):
        """Conversion de l'objet en chaîne de caractères
        
        Examples
        --------
        >>> mon_obj = ma_classe("toto")
        >>> print(mon_obj)
        Classe d'attribut_1 égal à toto
        """
        return f"Classe d'attribut_1 égal à {self.attribut_1}"

2.2 Fonction

Voici un modèle que vous pouvez utiliser pour documenter vos fonctions

def ma_fonction(param1, param2):
    """description_courte

    description_longue

    Parameters
    ----------
    param1 : type (str, int, float, list...)
        description_param1
    param2 : type
        description_param2

    Returns
    -------
    type_retour
        description_retour

    Examples
    --------
    >>> 1+1
    2
    >>> print("abc")
    abc
    >>> ma_fonction(xxx, yyy)
    valeur_esperee
    """
    return None


if __name__ == '__main__':
    a = ma_fonction(p1, p2)
    print(a)

2.3 TU avec pytest

import pytest
from mon_fichier import ma_fonction

@pytest.mark.parametrize(
    'param1, param2, resultat_attendu',
    [
        (1,       1,                2),
        (1,       2,                3),
        (2,       2,                4)
    ]
)
def test_ma_fonction(param1, param2, resultat_attendu):
    assert ma_fonction(param1, param2) == resultat_attendu

3 Exercices

    • dans votre Explorer à gauche apparait l’arborescence des dossiers et fichiers de Exercice_1

3.1 Exercice 1

    >>> a1 = Voiture('titine', 'bleue') 
    >>> a2 = Voiture('quatrelle','verte') 
    >>> a2.accelere(18) 
    >>> a3 = Voiture('bovelo','jaune')
    >>> a3.accelere(8)
    >>> print(a1)
    Voiture titine de couleur bleue à l'arrêt.
    >>> print(a2)
    Voiture quatrelle de couleur verte roule à 10km/h.
    >>> print(a3)
    Voiture bovelo de couleur jaune roule à 8km/h.

Pour lancer les tests, plusieurs possibilités :

  1. Coller ceci en bas de votre classe, puis cliquer sur l’icone éxéctuer ▶️
if __name__ == '__main__':
    import doctest    

    doctest.testmod(verbose=True)
  1. dans un terminal : python -m pytest --doctest-modules
  2. utiliser l’interface de VSCode (à gauche icone Testing en forme de fiole)

3.2 Exercice 2


Remarque

En supposant que c1 et c2 sont deux instances de Complexe, ces deux lignes de code font exactement la même chose et renvoient le même résultat :

  • c1.__add__(c2)
  • c1 + c2

3.3 Exercice 3