def mediatrice(self,p) : if self.y==p.y : return None m=self.milieu(p) co=(p.x-self.x)/(p.y-self.y) a=-co b=m.x*co+m.y return Droite(a,b)</code>
Segment
contenant comme attributs les 2 points extrêmes et d'implanter les méthodes mediatrice
et milieu
dans la classe Segment
., et
__init__ en était un example).
Cette méthode, nommée
__repr__ est utilisée automatiquement par Python pour fournir une représentation exécutable de l'objet.
Une méthode un peu similaire
__str__ fournit une représentation pour l'utilisateur, pas forcément exécutable.
Dans notre cas, si le point de coordonnées 5,3 s'affiche ainsi :
Point(5,3) nous avons à la fois une représentation lisible et exécutable.
Cet ajout nous permet de supprimer la méthode
affiche devenue inutile.
</WRAP>
<code python>
class Point
…
def repr(self) :
return 'Point('+str(self.x)+','+str(self.y)+')'
</code>
La méthode ''__repr__, comme
__str__ ne doit pas afficher mais doit retourner une chaîne de caractères.
</WRAP>
Voyons ce que nous pouvons faire à présent :
<code python>
»> p=Point(5,3)
»> p
Point(5,3)
»> print(p)
Point(5,3)
»> p1=Point(5,3)
»> p1
Point(5,3)
»> p2=Point(2,6)
»> p3=p1.milieu(p2)
»> p3
Point(3.5,4.5)
»> d=p1.mediatrice(p3)
»> d
<main.Droite object at 0x7fc12c1139d0>
»> d.a,d.b
(1.0, -0.5)
</code>
C'est l'occasion de rajouter une méthode
__repr__ à la classe
Droite aussi :
<code python>
class Droite :
def repr(self) :
return 'Droite('+str(self.a)+','+str(self.b)+')'
</code>
==== - Quelques méthodes en plus ====
Le cinquième postulat d'Euclide nous indique que par un point donné, il ne passe qu'une droite parallèle à une autre droite donnée.
C'est l'occasion pour nous d'écrire une nouvelle méthode dans la classe
Droite.
<code python>
class Droite
…
def parallele(self,p) :
d=Droite(self.a,self.b)
d.b=p.y-d.a*p.x
return d
</code>
Ajoutez des méthodes permettant de trouver :
- la perpendiculaire à une droite passant par un point
- l'intersection de deux droites.
(Solution)
vous pourrez ensuite vérifier que tout fonctionne correctement :
<code python>
»> d1=Droite(2,-3)
»> p1=d1.prendPoint(2)
»> p1
Point(2,1)
»> p2=Point(8,-3)
»> d3=d1.perpendiculaire(p2)
»> d3
Droite(-0.5,1.0)
»> d2=d3.perpendiculaire(p2)
»> d2
Droite(2.0,-19.0)
»> d1.parallele(p2)
Droite(2,-19)
»> p3=d1.intersection(d3)
»> p3
Point(1.6,0.20000000000000018)
»> m=p2.mediatrice(p3)
»> m
Droite(2.0,-11.0)
»> p2.milieu(p3)
Point(4.8,-1.4)
»> d1.parallele(p2.milieu(p3))
Droite(2,-11.0)
</code>
==== - Héritage ====
En plus des points du plan, nous désirons générer les points colorés. La couleur sera représentée par un triplet (r,v,b).
Tout ce que peut faire un point, un point coloré peut le faire : un point coloré est donc un cas particulier de point. C'est dans ce cas précis que nous utilisons l'héritage :
<code python>
class PointCouleur(Point) :
def init(self,x,y,col) :
super().init(x,y)
self.col=col
</code>
La classe PointCouleur
hérite de la classe Point
car c'est mentionné sur la ligne de déclaration de la classe.Nous écrivons une nouvelle méthode d'initialisation pour PointCouleur
et appelons à l'intérieur la méthode d'initialisation de la classe mère (grâce à super()
)
Voyons comment utiliser cette nouvelle classe :
<code python>
»> p1=PointCouleur(2,3,(255,0,0))
»> p2=PointCouleur(-5,1,(0,255,0))
»> p1.mediatrice(p2)
Droite(-3.5,-3.25)
</code>
La classe
PointCouleur hérite de la classe
Point et donc la méthode
mediatrice est disponible aussi pour les
PointCouleur.
En revanche l'affichage n'est pas satisfaisant :
<code python>
»> p1=PointCouleur(2,3,(255,0,0))
»> p1
Point(2,3)
</code>
Nous redéfinissons donc la méthode
__repr__ de la classe
PointCouleur :
<code python>
class PointCouleur(Point) :
def init(self,x,y,col) :
super().init(x,y)
self.col=col
def repr(self) :
return 'PointCouleur('+repr(self.x)+','+repr(self.y)+','+repr(self.col)+')'
</code>
L'affichage est maintenant différent selon le type de point :
<code python>
»> p1=PointCouleur(2,3,(255,0,0))
»> p2=PointCouleur(-5,1,(0,255,0))
»> p1
PointCouleur(2,3,(255, 0, 0))
»> p2
PointCouleur(-5,1,(0, 255, 0))
»> p1.milieu(p2)
Point(-1.5,2.0)
</code>
===== - Copie =====
Attention, l'affectation ne crée pas une copie d'un objet, mais une nouvelle référence sur l'objet :
<code python>
»> p=Point(1,2)
»> p1=p
»> p1
Point(1,2)
»> p1.x=42
»> p
Point(42,2)
</code>
Modifier
p ou
p1 c'est la même chose. Si on souhaite copier l'objet et en avoir deux versions, il faut utiliser le module
copy :
<code python>
»> import copy
»> p=Point(42,2)
»> p2=copy.copy(p)
»> p2
Point(42,2)
»> p2.x=99999
»> p2
Point(99999,2)
»> p
Point(42,2)
</code>
===== - Héritage ou non =====
Il est important de différencier si un objet fait partie d'un autre, ou si un objet est un cas particulier d'un autre. Lors de la conception de la classe
B, et si on dispose déjà de la classe
A, on se demande :
* si
B est une sorte de
A (comme un rectangle est un quadrilatère ou un point coloré est un point), alors
B doit probablement être une classe qui hérite de
A
* si
B possède un
A (comme un cercle possède un centre ou un segment deux points extrémités), alors
B doit probablement avoir un objet de type
A comme attribut
===== - Code pour démarrer les exercices =====
<file python geom.py>
class Point :
def milieu(self,p) :
return Point((self.x+p.x)/2,(self.y+p.y)/2)
def repr(self) :
return 'Point('+str(self.x)+','+str(self.y)+')'
def mediatrice(self,p) :
if self.y==p.y : return None
m=self.milieu(p)
co=(p.x-self.x)/(p.y-self.y)
a=-co
b=m.x*co+m.y
return Droite(a,b)
def init(self,x,y) :
self.x=x
self.y=y
class Droite :
def init(self,a,b) :
self.a=a
self.b=b
def appartient(self,p) :
if p.y==self.a*p.x+self.b : return True
return False
def prendPoint(self,x) :
y=self.a*x+self.b
return Point(x,y)
def repr(self) :
return 'Droite('+str(self.a)+','+str(self.b)+')'
def parallele(self,p) :
d=Droite(self.a,self.b)
d.b=p.y-d.a*p.x
return d
class PointCouleur(Point) :
def init(self,x,y,col) :
super().init(x,y)
self.col=col
def repr(self) :
return 'PointCouleur('+repr(self.x)+','+repr(self.y)+','+repr(self.col)+')'
</file>
===== - Tips =====
la méthode mro()
(method resolution order) utilisable sur une classe indique l'ordre dans lequel les classes (celle utilisée et ses ancètres) sont parcourues pour trouver la méthode à exécuter :>>> PointCouleur.mro()
[<class '__main__.PointCouleur'>, <class '__main__.Point'>, <class 'object'>]
===== - Documentation =====
Il est important de bien documenter les classes et méthodes grâce aux docstrings. Voici le type de renseignements qu'on obtient ensuite avec
help'' :>>> help(poogeom)
Help on module poogeom:
NAME
poogeom
DESCRIPTION
Module poogeom : illustration de quelques concepts
de programmation orientée objets avec des objets géométriques
CLASSES
builtins.object
Droite
Point
PointCouleur
Segment
class Droite(builtins.object)
| Classe représentant des droites non verticales du plan
|
| Methods defined here:
|
| __init__(self, a, b)
| Une droite non verticale est définie pas son coefficient
| directeur et l'ordonnée à l'origine
|
| __repr__(self)
|
| contient(self, p)
| Indique si le point p appartien à self
|
| intersection(self, d)
| Renvoie le point d'intersection de self et d
|
| parallele(self, p)
| Renvoie la droite parallèle à self passant par p
|
| perpendiculaire(self, p)
| Renvoie la droite perpendiculaire à self passant par p
|
| prendPoint(self, x)
| Renvoie le point d'abcisse x situé sur self
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
class Point(builtins.object)
| Classe pour représenter un point du plan
|
| Methods defined here:
|
| __init__(self, x, y)
| Un point est défini par ses deux coordonnées
|
| __repr__(self)
|
| mediatrice(self, p)
| Renvoie la médiatrice de self et p
|
| milieu(self, p)
| Renvoie le point milieur de self et p
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
class PointCouleur(Point)
| Classe représentant un point du plan d'une certains couleur
|
| Method resolution order:
| PointCouleur
| Point
| builtins.object
|
| Methods defined here:
|
| __init__(self, x, y, col)
|
| __repr__(self)
|
| ----------------------------------------------------------------------
| Methods inherited from Point:
|
| mediatrice(self, p)
| Renvoie la médiatrice de self et p
|
| milieu(self, p)
| Renvoie le point milieur de self et p
|
| ----------------------------------------------------------------------
| Data descriptors inherited from Point:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
class Segment(builtins.object)
| Classe pour représenter un segment de droite
|
| Methods defined here:
|
| __init__(self, p1, p2)
| Un segment est défini par ses deux extrémités
|
| mediatrice(self)
| Droite médiatrice d'un segment
|
| milieu(self)
| Milieu d'un segment
|
| support(self)
| Renvoie la droite support du segment
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)