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.
Une classe est une construction de langage qui permet de réunir les données et les fonctions agissant sur ces données.
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
__init__ en python), appelée automatiquement à la création
d’une instanceen 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)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 = 0Une valeur par défaut peut être donnée aux attributs dans la déclaration (cf attributs de classe).
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 += dyLe 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)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.
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 += montantEn 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 :
self._x) indique par
convention qu’un attribut est interne à la classe (rien
ne l’empêche techniquement d’être utilisé)self.__x) renomme l’attribut en
_Robot__x : il reste accessible via
r2d2._Robot__x, mais plus par erreurElles permettent (entre autres) d’implémenter les opérations “classiques” en python :
https://docs.python.org/fr/3/reference/datamodel.html
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é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).
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)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) # 2De 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 = []).
Définir et tester une classe Contact permettant de
nom,
prenom, email)NOM Prénom <email> via la méthode
__str__cible de caractères donnée via la méthode
search(self, cible)Définir et tester une classe Carnet permettant de
Contact)ajoute(self, nom, prenom, email)search(self, cible)Définir et tester une classe Heure qui permet de
heure,
minute, seconde__str__ l’heure sous forme de
chaîne de caractères au format 8h48m13s__gt__(self, autre_heure) (ou inférieure via
__lt__)x secondes à une heure via la méthode
ajoute(self, x)
x à l’attribut secondeseconde >= 60, ajouter seconde // 60
à minute et mettre seconde = seconde % 60minute >= 60, ajouter minute // 60 à
heure et mettre minute = minute % 60heure >= 24, mettre
heure = heure % 24Définir et tester une classe Date qui permet de
annee, mois, jour__str__ la date sous forme de
chaîne de caractères au format 28 mai 1978inc; utiliser un tableau
nb_jours donnant le nombre de jours dans chaque mois pour
passer au mois suivant et à l’année suivante si besoin__eq__(self, autre_date)Définir et tester une classe Point permettant de :
x, y et
z d’un pointcoords() les coordonnées du point sous la
forme d’un triplet (x, y, z)dx, dy,
dz via la méthode bouge(self, dx, dy, dz)Définir et tester une classe Ligne permettant de :
a et b (instances de
Point) qui définissent la lignedx, dy et
dz via la méthode translate(self, dx, dy, dz)
(la translation agit sur les 2 points, utiliser la méthode
bouge de Point)p entre
a et b (si
,
on est sur
,
si
on est sur
,
et de manière générale
pour chaque coordonnée
)
via la méthode curseur(self, p), sous la forme d’un triplet
(x, y, z)