In [ ]:
Nom = ""
Prénom = ""

Programmation OO et encapsulation¶

1 - Encapsulation¶

Un problème de la programmation impérative (utilisation de variables globales) est que toutes les parties d'un programme dépendent potentiellement des autres. On parle de "programmation spaghetti".

Cela augmente considérablement la complexité d'un gros programme, rendant difficile de le maintenir et de le modifier sans introduire de régressions.

Cela rend aussi difficile le travail en équipe, chacun devant avoir conscience du fonctionnement du programme entier.

Une réponse possible a ces problèmatiques est l'encapsulation des données : le programme est divisée en plusieurs parties, qui chacune a un accès exclusif à ses données (variables).

Python propose plusieurs solutions pour encapsuler les données :

  • l'utilisation des modules : c'est la programmation modulaire;
  • la programmation orientée objet.

2 - Interface et implémentation¶

Quand un programme est divisé en plusieurs parties qui chacunes encapsulent leurs données, ces parties communiquent ensemble via des fonctions.

La spécification des ces fonctions constitue l'interface de la partie (classe ou module).

Le code à l'intérieur de chaque partie est l'implémentation de l'interface. Ce code est complètement indépendant du code des autres parties.

Par exemple, l'interface de la classe Vecteur est constituée de :

class Vecteur:

    def __init__(self, x, y):
        """
        Construit un vecteur de coordonnées (x, y).
        :param x: (float) l'abscisse du vecteur
        :param y: (float) l'ordonnée du vecteur
        :return: (Vecteur object)
        """
        pass
        
    def norme(self):
        """
        Renvoie la norme du vecteur self
        :return: (float) la norme du vecteur self
        """
        pass
    
    def multiplie(self, k):
        """
        Multiplie le vecteur self par le scalaire k
        :param k: (float)
        """
        pass

Le code à l'intérieur de ces fonctions constitue l'implémentation de la classe Vecteur.

3 - Attributs privés¶

Une attribut privé est un attribut d'une classe auquel seules les méthodes de cette classe ont accès.

En Python, un attribut privé a un nom qui commence par _ (c'est une convention, le language n'empêche pas l'accès).

Si un accès en lecture d'un attribut privé est nécessaire de l'extérieur de la classe, on rajoute à l'interface une méthode (getter) qui renvoie la valeur de la variable.

De la même façon, une méthode (setter) peut modifier la valeur d'une variable privée.

Une classe doit avoir un maximum d'attributs privés (de préférence tous).

Par exemple, la classe Vecteur devient :

In [ ]:
class Vecteur:
    
    def __init__(self, x, y):
        """
        Construit un vecteur de coordonnées (x, y).
        :param x: (float) l'abscisse du vecteur
        :param y: (float) l'ordonnée du vecteur
        :return: (Vecteur object)
        """
        self.set_coordonnees(x, y)
            
    def norme(self):
        """
        Renvoie la norme du vecteur self
        :return: (float) la norme du vecteur self
        """
        return math.sqrt(self._x ** 2 + self._y ** 2)
        
    def multiplie(self, k):
        """
        Multiplie le vecteur self par le scalaire k
        :param k: (float)
        """
        self._x *= k
        self._y *= k

    def get_coordonnees(self):
        """
        Renvoie les coordonnées du vecteur (getter)
        :return: (tuple) les coordonnées du vecteur self
        """
        return self._x, self._y
            
    def set_coordonnees(self, x, y):
        """
        Définie les coordonnées du vecteur self
        :param x: (float) l'abscisse du vecteur self
        :param y: (float) l'ordonnée du vecteur self
        """
        self._x = x
        self._y = y

4 - Objets et références¶

Comme les listes, les objets sont passés par référence dans les fonctions. Ainsi, si une fonction modifie un objet reçu en argument, les modifications persistent en dehors de la fonction.

Par exemple, la fonction suivante annule un vecteur :

In [ ]:
def annule(v):
    v.set_coordonnees(0.0, 0.0)

On peut constater que le vecteur v est passé par référence :

In [ ]:
v = Vecteur(1.0, 2.0)
v.get_coordonnees()
In [ ]:
annule(v)
v.get_coordonnees()

5 - Exercices sur les vecteurs¶

a - Somme¶

Rajouter à la classe Vecteur une méthode add qui prend en argument un vecteur u et l'ajoute à self.

b - Produit scalaire¶

Rajouter à la classe Vecteur une méthode scalaire qui prend en argument en vecteur u et renvoie le produit scalaire des vecteurs u et self.

c - Déterminant¶

Rajouter à la classe Vecteur une méthode det qui prend en argument en vecteur u et renvoie le déterminant des vecteurs u et self.

Le déterminant de deux vecteurs $\vec{u}(x, y)$ et $\vec{v}(x', y')$ est :

$$ \det(\,\vec{u}, \vec{v}\,) = xy' - x'y $$

In [ ]:
# Insérez votre code ici
In [ ]:
# Test de la méthode add
import math
u = Vecteur(1.0, -2.0)
v = Vecteur(4.0, 1.0)
v.add(u)
vx, vy = v.get_coordonnees()
assert math.isclose(vx, 5.0) and math.isclose(vy, -1.0)
In [ ]:
# Test de la méthode scalaire
u = Vecteur(1.0, -2.0)
v = Vecteur(4.0, 1.0)
assert math.isclose(u.scalaire(v), 1.0*4.0 + (-2.0)*1.0)
In [ ]:
# Test de la méthode det
u = Vecteur(1.0, -2.0)
v = Vecteur(4.0, 1.0)
assert math.isclose(u.det(v), 1.0*1.0 - (-2.0)*4.0)

6 - Exercice sur les points¶

Implémenter la classe Point dont voici l'interface :

class Point:

    def __init__(self, x, y):
        """
        Contruit un point de coordonnées (x, y)
        :param x: (float) Abscisse du point self
        :param y: (float) Ordonnée du point self
        """
        pass
    
    def get_coordonnees(self):
        """
        Renvoie les coordonnées du point self
        :return: (tuple)
        """
        pass
    
    def distance(self, m):
        """
        Renvoie la distance entre les points self et m
        :param m: (objet Point)
        :return: (float)
        """
        pass
   
    def translate(self, v):
        """
        Applique au point self la translation de vecteur v
        :param v: (objet Vecteur)
        """
        pass
        
    def dup(self):
        """
        Duplique le point self
        :return: (objet Point) un point ayant les même coordonnées que self
        """
        pass
        
    def vect(self, m):
        """
        Renvoie le vecteur qui translate le point self sur le point m
        :param m: (objet Point)
        :return: (objet Vecteur)
        """
        pass
In [ ]:
# Insérez votre code ici
In [ ]:
import math
p = Point(2.0, 5.0)
m = Point(-1.0, 3.0)
v = Vecteur(6.0, -2.0)
assert p.get_coordonnees() == (2.0, 5.0)
assert math.isclose(p.distance(m), math.sqrt(3.0**2 + 2.0**2))
p.translate(v)
assert p.get_coordonnees() == (8.0, 3.0)
n = p.dup()
assert isinstance(n, Point)
assert n.get_coordonnees() == p.get_coordonnees()
u = n.vect(m)
assert isinstance(u, Vecteur)
assert u.get_coordonnees() == (-9.0, 0.0)

7 - Exercice : polygônes¶

Il s'agit dans cette partie de programmer une classe Polygone, qui satisfait les contraintes suivantes :

  • les sommets seront représentés par des objets Point;
  • une méthode permet de rajouter un sommet;
  • une méthode permet d'obtenir le nombre de sommet du polygône;
  • une méthode permet d'obtenir les coordonnées du n-ième sommet;
  • une méthode permet d'obtenir le périmètre du polygône;
  • une méthode permet de translater l'intégralité du polygône d'un vecteur v;
  • une méthode affiche le polygône (en vous inspirant du programme de la cellule suivante);
  • une méthode indique si le polygône est convexe (c'est le cas si les déterminants de deux côtés (vecteur) successifs sont tous du même signe);
  • une méthode calcule son aire.
In [ ]:
import matplotlib.pyplot as plt

liste_x = [-1.0, 2.0, 5.0, 3.0, -1.0] # liste des abscisses
liste_y = [0.0, 4.5, 2.1, 0.4, 0.0]   # liste des ordonnées
plt.plot(liste_x, liste_y, 'b-o')
plt.show()
In [ ]:
# Insérez votre code ici