Programme

Partie “structures de données”

Contenus : Vocabulaire de la programmation objet :

Capacités attendues :

Commentaires :

On n’aborde pas ici tous les aspects de la programmation objet comme le polymorphisme et l’héritage.

Vocabulaire de la POO

Notion de classe

Définition

Une classe est une construction de langage qui permet de réunir les données et les fonctions agissant sur ces données.

Exemple


class Robot:
    # Constructeur (appelé quand la classe est instanciée)
    def __init__(self):
        # x : attribut de l'instance
        self.x = 0
        # y : attribut de l'instance
        self.y = 0

    # méthode "bouge" de la classe Robot
    def bouge(self, dx, dy):
        self.x += dx
        self.y += dy

    # méthode "position" de la classe Robot
    def position(self):
        return (self.x, self.y)
# walle est un objet, instance de la classe Robot
walle = Robot()
# evee est un objet, autre instance de la classe Robot
evee = Robot()

walle.bouge(3,3)
evee.bouge(1,2)

print("WallE", walle.position())
print("Evee",  evee.position())
print("x WallE", walle.x) # accès direct

Représentation en mémoire

Représentation en mémoire

Vocabulaire

en python :

class NomClasse():  # définition de classe, CamelCase
    def __init__(self): # constructeur, self est une référence à l'instance
        self.attribut = .. # attribut initialisé
    def methode(self): # méthode
        ...
obj = NomClasse() # instanciation (obj = instance de NomClasse)
obj.attribut # accès direct à l'attribut
obj.methode() # appel d'une méthode, self est passé automatiquement
# équivalent à NomClasse.methode(obj)

Attributs

Les attributs sont initialisés dans le constructeur (méthode __init__).

Ils peuvent être déclarés avant le constructeur (pour indiquer leur type).

class Robot:
    x: int
    y: int
    def __init__(self):
        self.x = 0
        self.y = 0

Une valeur par défaut peut être donnée aux attributs dans la déclaration (cf attributs de classe).

Méthodes

Les méthodes sont des fonctions définies dans la classe, qui agissent sur une instance de la classe. Le premier argument d’une méthode est toujours self, qui est une référence à l’instance sur laquelle la méthode est appelée.

Les attributs de l’instance sont accessibles via self.attribut.

class Robot:
    ...
    def bouge(self, dx, dy):
        self.x += dx
        self.y += dy

Le constructeur et les méthodes peuvent prendre d’autres paramètres après self :

class Robot:
    def __init__(self, x, y):
        self.x = x
        self.y = y

c3po = Robot(5, 2) # appelle __init__(c3po, 5, 2)

Objets et références

Une variable ne contient pas l’objet lui-même, mais une référence vers l’objet : une affectation ne copie pas l’objet.

a = Robot()
b = a          # a et b désignent le même objet
b.bouge(1, 1)
print(a.position()) # (1, 1) : a a aussi "bougé"
c = Robot()    # c est un autre objet
print(a == c)  # False (sans __eq__, == compare les références)

De même, un objet passé en argument à une fonction peut être modifié par cette fonction.

Encapsulation

Il est préférable de ne pas agir directement sur les données d’un objet, mais de passer par des méthodes, qui jouent le rôle d’interface entre l’objet et le programme. On peut ainsi changer la représentation interne de l’objet sans changer le reste du programme, et contrôler les modifications.

class Compte:
    def __init__(self):
        self.__solde = 0
    def get_solde(self):        # accesseur (getter)
        return self.__solde
    def depose(self, montant):  # modification contrôlée
        if montant > 0:
            self.__solde += montant

En python, on protège un attribut en faisant débuter son nom par un double underscore (self.__x par exemple) :

class Robot:
    def __init__(self):
        self.__x = 0
        self.__y = 0
r2d2 = Robot()
r2d2.__x # ===> AttributeError: 'Robot' object has no attribute '__x'

Remarque : certains langages (Java, C++, …) permettent de définir des attributs privés (non accessibles en dehors de la classe) et des attributs publics (accessibles en dehors de la classe). En python, tous les attributs sont publics, on peut seulement simuler des attributs privés :

Infos utiles

Méthodes “magiques” / spéciales

Référence

Elles permettent (entre autres) d’implémenter les opérations “classiques” en python :

https://docs.python.org/fr/3/reference/datamodel.html

Exemple

class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __add__(self, p):
        return Point(self.x + p.x, self.y + p.y)

p1 = Point(2, 4)
p2 = Point(3, 5)
p = p1 + p2
print(p.x, p.y) # 5 9

Méthodes spéciales courantes

méthode appelée par
__init__(self, ...) Point(...)
__str__(self) str(p), print(p)
__repr__(self) repr(p), console
__eq__(self, autre) p == autre
__lt__(self, autre) p < autre
__gt__(self, autre) p > autre
__add__(self, autre) p + autre
__sub__(self, autre) p - autre
__len__(self) len(p)
class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __str__(self):
        return f"({self.x}, {self.y})"
    def __eq__(self, p):
        return self.x == p.x and self.y == p.y

print(Point(2, 4))               # (2, 4)
print(Point(2, 4) == Point(2, 4)) # True

__str__ doit renvoyer une chaîne de caractères (et pas l’afficher).

Attributs de classe

Utilisation

Un attribut de classe est défini au niveau de la classe (il est partagé par toutes les instances), mais sert aussi de valeur par défaut aux attributs des instances. On peut donc s’en servir pour stocker une information au niveau de la classe elle-même (bien) et/ou pour donner une valeur par défaut aux attributs (pas bien, mais couramment utilisé dans les faits)

class Point():
    cpt = 0
    x = 0
    def __init__(self, y):
        self.y = y
        Point.cpt += 1
    def pos(self):
        print(self.x, self.y)

Exemple d’utilisation

p1 = Point(2)
p1.pos() # 0 2
p1.x = 2
p1.pos() # 2 2
Point.x = 3
p1.pos() # 2 2
p2 = Point(3)
p2.pos() # 3 3
print(Point.cpt) # 2

De manière générale, attention aux valeurs par défaut de type mutable (listes, objets, …) : elles sont partagées par toutes les instances.

class Panier():
    articles = []          # à éviter : liste partagée
    def ajoute(self, a):
        self.articles.append(a)

p1 = Panier()
p2 = Panier()
p1.ajoute("pomme")
print(p2.articles) # ['pomme'] !!

Solution : initialiser la liste dans le constructeur (self.articles = []).

Exercices

Contact et carnet adresses

Contact

Définir et tester une classe Contact permettant de

Carnet

Définir et tester une classe Carnet permettant de

Date et heure

Heure

Définir et tester une classe Heure qui permet de

Date

Définir et tester une classe Date qui permet de

Ligne et points

Point

Définir et tester une classe Point permettant de :

Ligne

Définir et tester une classe Ligne permettant de :