Classes et POO
Classes
Python permet le paradigme Programmation Orienté Objet (POO).
Les objets groupent des données et des méthodes
(fonctions) logiquement liées.
Par exemple, pour objets de type list
>>> nombres = [3, 1, 2]
>>> nombres.reverse()
>>> nombres.sort()
>>> nombres.pop()
Classes
Python permet le paradigme Programmation Orienté Objet (POO).
Les objets groupent des données et des méthodes
(fonctions) logiquement liées.
Par exemple, pour objets de type list
>>> nombres = [3, 1, 2]
>>>
Données
nombres.reverse()
>>>
nombres.sort()
>>> nombres.pop() Fonctions
Classes
Paradigme fonctionnel/procédural :
vec1 = (3, 4)
vec2 = (1, -3)
def longueur(v):
return math.sqrt(v[0]**2 + v[1]**2)
longueur(v
long1 ec1) =
long2 longueur(v
= ec2)
print(vec1[0]) # Affiche '3'
Classes
Paradigme fonctionnel/procédural :
Données
vec1 = (3, 4)
vec2 = (1,
-3)
Fonctions
def longueur(v):
return math.sqrt(v[0]**2 + v[1]**2)
longueur(v
long1 ec1) =
long2 longueur(v
= ec2)
print(vec1[0]) # Affiche '3'
Classes
Paradigme OO : définir un nouveau type Vecteur pour grouper les
données d'un vecteur et le fonction qui agissent dessus.
Vecteur
x
y
longueur()
Classes
Paradigme OO : définir un nouveau type Vecteur pour grouper les
données d'un vecteur et le fonction qui agissent dessus.
vec1 = Vecteur(3, 4) # création d'un objet Vecteur
vec2 = Vecteur(1, -3) # création d'un objet
Vecteur
ur() =
long1 vec2.longue
long2 ur()
=
vec1.longue
print(vec1.x) # affiche 3
Classes
Paradigme fonctionnel/procédural
chat2
chat1 =
('Felix', ('Cléo',
5) = 7)
def affiche_age(c):
print('{} a {} ans'.format(c[0], c[1]))
affiche_age(chat1)
affiche_age(chat2)
Classes
Paradigme OO :
Chat
= , 7)
chat1 Chat('Felix nom
age
chat2 ', 5) =
Chat('Cléo' affiche_age()
chat1.affiche_age()
print(chat.nom)
Classes
Ces nouveaux types s'appellent classes.
Une nouvelle classe est définie par le mot-clé class.
class NomDeLaClasse:
<instruction 1>
<instruction 2>
.
.
.
<instruction N>
Classes
La définition d'une classe :
class Vecteur:
'''Cette classe représente un
vecteur.''' def __init (self, a, b):
self.x = a
self.y = b
def longueur(self):
return math.sqrt(self.x**2 +
self.y**2) Création d'instances de la classe = objets :
vec1 = Vecteur(3, # appel de init (..., 3, 4)
4)
# aucun 'self'
argument pour
print(vec1.x) # Affiche '3'
Classes
class Vecteur:
'''Cette classe représente un vecteur.'''
def init s
self.x e
self.y l
f
, :
a =
, a
b = b
)
def longueur(self):
return math.sqrt(self.x**2 + self.y**2)
Finalement, on peut appeler les méthodes sur l'objet :
long = Vecteur.longueur(vec1)
Syntaxe alternative, plus pratique (sans argument explicite pour self):
long = vec1.longueur()
Classes
class Chat:
'''Cette classe représente un chat.'''
def init (self, n, a):
self.nom = n
self.age = a
def affiche_age(self):
print('{} a {} ans'.format(self.nom,
self.age)) def parle(self):
print('miaou')
monchat = Chat('Felix', 5)
monchat.affiche_age()
monchat.parle()
Classes
Deux syntaxe pour appeler les méthodes :
● objet.methode() syntaxe courante (implicitement transformée dans
la deuxième)
Classe.methode(objet
) ●
class Chat :
def parle(self):
print('miaou')
monchat = Chat()
# Ces deux appels #
monchat.parle() sont équivalents
Chat.parle(monchat)
Classes
Quelques conventions.
●nom des classes : en « CamelCase »
Vecteur
PlanCartesien
Chat
MainWindow
●nom des objet : en minuscule (avec tirets bas)
vec = Vecteur(1, 2)
vec_longue = Vecteur(35,-43)
plan_cartesien = PlanCartesien()
chat1 = Chat('Felix', 5)
main_win = MainWindow()
Classes
Les méthodes ont toujours un premier argument qui représente, dans la
méthode, l'objet = l'instance particulière.
Par convention, on l'appelle self.
...
def presente(self):
print('{} a {} ans'.format(self.nom,
self.age)) def parle(self, n):
print('miaou ' * n)
def parle_beaucoup(self):
self.parle(10)
...
Classes
On peut définir des attributs pour les classes aussi (class attributes = attributs de classe),
par affectation.
Ils sont en commun pour toutes les objet crée par la classe.
Souvent utilisés pour les « constantes » du type.
class Cercle:
PI = 3.14159
def init (self, r):
self.rayon = r
def aire(self):
return Cercle.PI * self.rayon ** 2
print('La valeur de PI est {}'.format(Cercle.PI))
c = Cercle(2.5)
print('rayon = {}, aire = {}'.format(c.rayon, c.aire()))
Classes
Un objet peut être composé par des autres
Rectangle
objets. Exemple : un rectangle est défini par
deux points.
b = Vecteur(5, 4)
class Rectangle:
a = Vecteur(1, 2)
def init (self, p1, x=1
p2): rect = Rectangle(a, y=2
self.p1 = p1 b)
self.p2 = p2 print(rect.aire())
def aire(self): p1 =p2 = aire() Vecteur
largeur = self.p2.x – x=5
self.p1.x hauteur = y=4
self.p2.y – self.p1.y
return largeur *
hauteur Vecteur
Classes
Il n'y a pas d'attributs privés en Python, tout est toujours accessible.
Une convention est de préfixer avec un seule tiret bas les attributs qu'on
veut protéger. Mais c'est juste une « suggestion » aux programmateurs.
class Chat:
def init (self):
self._age = 0
def set_age(self, age):
self._age = max(age, 0)
def get_age(self):
return self._age
Ce code suggère au programmateur qui veut utiliser la classe Chat, de ne
pas toucher à _age et utiliser seulement set_age() et get_age().
Méthodes spéciales et
opérateurs
Méthodes spéciales
On peut utiliser les opérateurs + - * / < > etc... avec des objets. Par exemple,
pour l'operateur + il faut définir une méthode add ()
class Vecteur:
...
def add (self, other):
x = self.x + other.x
y = self.y + other.y
return Vecteur(x, y)
...
>>> v1 = Vecteur(3, 5)
>>> >>> Vecteur(2, -1) v1. add (v2) # v1
v2 v1 + est ‘self’ et v2
= est ‘other’
v2 # équivalent à
Vecteur(5, 4)
Méthodes spéciales
+ add - sub * mul /
truediv
// < gt (greater than) le
> (less or equal) ge
<= >= (greater or equal)
floordiv
lt (less than)
== eq ...
Héritage
Héritage
Création de nouvelles classes à partir de classes déjà définies :
class ClasseDerivee(ClasseDeBase):
<instruction 1>
<instruction 2>
.
.
.
<instruction N>
Si un attribut n'est pas trouvé dans la définition de la classe dérivée,
Python cherche récursivement dans la classe de base etc.
Héritage class B(A):
Pour dériver la classe B de la def mB(self):
class A: print('B')
class A: b = B()
def mA(self): b.mB()
print('A')
mA
B
mB
b.mA() # Les attributs de A sont accessibles
Héritage
Les classes dérivées peuvent remplacer les méthodes de la classe de
base. class A:
def m(self):
print('A')
B
class B(A): m
def m(self):
print('B')
Héritage
b = B()
b.m() # Affiche “B” class A:
A
m
def f(self):
self.m() f
m
def m(self):
print('A')
B
class B(A): m
def m(self):
print('B')
b = B()
b.f() # Affiche “B”
A
Héritage
Si jamais une méthode de B veut exécuter la méthode originale de A ( chain-up) il faut
utiliser la syntaxe alternative pour appeler les méthodes :
nomclasse.methode(objet)
class A:
def met(self):
print('A')
class B(A):
def met(self):
A.met(self)
print('B')
x = B()
x.met() # Affiche “A” et “B”
Héritage
Alternative : la fonction super()
class A:
def met(self):
print('A')
class B(A):
def met(self):
super().met()
print('B')
x = B()
x.met() # Affiche “A” et “B”
Héritage
On trouve souvent le chain-up dans les méthode d'initialisation :
class A:
def init (self, x):
self.x = x
B(A):
class
def init (self, x):
super().init (x)
self.foo = 'bar'
b = B(5)
Ici b.x == 5 et b.foo == 'bar'.
Etudiant(Personne):
Héritage class
Professeur(Personne):
Personne
prenom
nom
def init (self,
class Personne:
prenom, nom, cours):
def init (self, super(). init (prenom,
prenom, nom):
nom) Professeur
self.prenom = prenom cours
self.cours = cours
self.nom = nom Etudiant numero
class
def init (self, prenom, nom, numero):
super(). init (prenom, nom)
self.numero = numero
p = Professeur('Pierre', 'Curie', 'Physique') e
= Etudiant('Maria', 'Skłodowska', '0123456789')
Héritage
class C(A,
C
class A: B): B
bar
multiple pass
foo = 'A'
class B:
A
bar = 'B' foo
print(C.foo, C.bar) # Affiche “A B”
Héritage class B(A): C):
pass
pass
class A: class C:
multiple foo = 'C' print(D.foo)
foo = 'A' class D(B,
Python utilise
l'algorithme «
depth first » :
A on cherche en
foo
profondeur A
foo
dans une
B
branche avant
B
de passer à la
suivante
D
C
D
foo
C
Héritage foo
D→B→A Héritage
multiple →C
pass
multiple class C(A):
Problème avec le « foo = 'C'
diamant »
class D(B, C):
pass
class A: D
foo = 'A' C
print(D.foo) # foo
class B(A): ???
A
foo