Académique Documents
Professionnel Documents
Culture Documents
1 Objets classes
On considère ici que vous avez des notions de langage objets. En particulier,
vous savez ce qu’est une classe, un attribut et une méthode. Nous n’aborderons
pas ici les notions de méta-classes (des classes de classes), présentes dans python,
et laissons le soin au lecteur intéressé de lire la documentation présente sur le
site de python.
help(UneClasse)
print UneClasse
print dir(UneClasse)
La fonction dir est très utile, elle décrit tout ce que contient un objet. On
pourra essayer dir() pour voir tout ce que contient l’interpréteur, ou encore
dir( builtins ) pour voir, entre autres, les fonctions de bases de python.
Intéressons-nous pour l’instant à deux variables internes particulières (nous
verrons la plupart des autres au cours de ce cours) :
1
print UneClasse.__name__ #nom de la classe
print UneClasse.__doc__ #documentation de la classe
print UneClasse.__module__ #module dans lequel la classe a été crée
print UneClasse.__bases__ #parents direct de la classe
x = UneClasse()
help(x)
print x
print dir(x)
Définit une variable affectée à x. La liste des variables d’un objet est définie
dans le dictionnaire dict .
print x.__dict__
print x.__dict__["titre"]
x.__dict__["titre"] = "Cure toujours"
print x.titre
2
Tout objet python (une classe, un module, ...) possède une variable dict
qui regroupe tout ce que déclare cet objet (on appelle ça un namespace). On
peut s’en servir pour accéder à une variable, la modifier, ou en créer une autre
(la plupart du temps).
print UneClasse.__dict__
x.__dict__["paroles"] = "Oh non non non j’ai bronzé,\
mes potes voudront plus me parler," #idem que x.paroles =
2 Méthodes
Un des intérêt d’utiliser le formalisme objet est que l’on peut attribuer des
méthodes spécifiques aux aux instances d’une classe. Pour les objets liste que
nous avons manipulés les cours précédents par exemple, la méthode append
permet d’ajouter un élément en fin de liste. Il existe en python trois grandes
familles de méthodes. Les méthodes prédéfinies commençant et finissant par
“ ” (comme init ou len ), les méthodes programmées et les méthodes
de classe.
A part pour les fonctions de classes, le premier paramètre des méthodes est
toujours l’objet qui invoque la fonction. Par convention on le nommera self.
On peut également toujours invoquer une méthode en utilisant la classe elle-
même (le premier argument étant l’objet considéré), comme le montre l’exemple
suivant :
>>> x = list()
>>> x.append(1)
>>> x
[1]
>>> list.append(x, "un autre")
>>> x
[1, ’un autre’]
3
class UneClasse(object) :
""" une classe en construction
"""
def __init__(self, letitre="Cure Toujours"):
self.titre = letitre
Maintenant :
>>> x = UneClasse()
>>> print x.titre
Cure Toujours
>>> y = UneClasse("Goldorak est mort")
>>> print y.titre
Goldorak est mort
4
class UneClasse(object) :
""" une classe en construction
"""
titre = "C’est la vie, Cui Cui" #attribut de classe
On peut alors :
>>> x = UneClasse()
>>> x.titre
"C’est la vie, Cui Cui"
>>> y = UneClasse()
>>> y.titre
"C’est la vie, Cui Cui"
>>> UneClasse.titre = "Cure Toujours"
>>> print y.titre
Cure Toujours
>>> print x.titre
Cure Toujours
Attention cependant. Si vous définissez une variable dans un objet ayant le
même nom que la variable de classe, celle-ci sera masquée :
>>> print x.titre
Cure Toujours
>>> x.titre = "Goldorak est mort" #création d’une nouvelle variable !
>>> print x.titre
Goldorak est mort
>>> print y.titre
Cure Toujours
>>> del x.titre #suppression de la variable de l’instance
>>> print x.titre
Cure Toujours
Les variables de classes et d’instances ne sont pas dans le même namespace
(l’un est dans UneClasse. dict , l’autre dans x. dict ). Si Deux noms
coı̈ncide, c’est la variable d’instance qui est privilégiée.
Pour les méthodes de classes, tout se passe comme pour les méthodes d’instances
saut que le premier paramètre n’est plus une instance, mais une classe. Le nom
de ce paramètre est par convention cls. On peut ainsi reprendre l’exemple
précédent en utilisant une méthode de classe :
class UneClasse(object) :
""" une classe en construction
"""
titre = "C’est la vie, Cui Cui" #attribut de classe
def changeTitre(cls, titre):
cls.titre = titre
changeTitre = classmethod(changeTitre) #creation de la méthode de classe
5
Et là :
>>> x = UneClasse()>>> y = UneClasse()
>>> x.titre
"C’est la vie, Cui Cui"
>>> y.titre
"C’est la vie, Cui Cui"
>>> x.changeTitre("Cure toujours")
>>> x.titre
’Cure toujours’
>>> y.titre
’Cure toujours’
Pour une méthode de classe, c’est la classe de l’objet qui est passée en
paramètre (x. class ), plus l’objet lui-même.
class UneClasse(object) :
""" une classe en construction
"""
def maMethodeStatique(titre):
print titre
maMethodeStatique = staticmethod(maMethodeStatique)
Pour une méthode statique, qu’on l’appelle via la classe ou un objet n’a
aucune importance.
>>> x = UneClasse()
>>> x.maMethodeStatique("ici")
ici
>>> UneClasse.maMethodeStatique("ici")
ici
x.changeReponse("42")
print x.donneReponse()
6
x.reponse = 42
print x.reponse
Si les opérations à effectuer sont plus complexes que juste affecter une valeur
à un attribut, python permet de masquer les appels de fonction via la commande
properties
class UneClasse(object) :
""" une classe en construction
"""
def __init__(self, titre = "rien"):
self.__titre = titre
self.longueur = len(titre)
def gettitre(self):
return self.__titre
def settitre(self, titre):
self.__titre = titre
self.longueur = len(titre)
titre = property(gettitre, settitre)
>>> x = UneClasse()
>>> print x.titre
rien
>>> print x.longueur
4
>>> x.titre = "Guerre et Paix"
>>> print x.titre
Guerre et Paix
>>> print x.longueur
14
3 Héritage multiple
Python autorise l’héritage multiple et utilise une règle appelée diamond rule
pour régler les conflits.
class A(object):
classe = "A"
class B(object):
classe = "B"
7
Pour savoir qui est quoi, on peut utiliser la fonction issubclass() ou
l’attribut de classe bases (les supérieurs directs) ou encore la méthode de
classe subclasses (les successeurs directs) :
>>> A.__bases__
(<type ’object’>,)
>>> B.__bases__
(<type ’object’>,)
>>> C.__bases__
(<class ’__main__.A’>, <class ’__main__.B’>)
>>> A.__subclasses__()
[<class ’__main__.C’>]
>>> issubclass(C, A)
True
>>> issubclass(C, object)
True
>>> C.__mro__
(<class ’__main__.C’>, <class ’__main__.A’>, <class ’__main__.B’>,
<type ’object’>)
class A(object):
classe = "A"
class B(A):
pass
class C(object):
classe = "C"
class D(B,C):
pass
print D.classe
class Cprim(A):
classe = "diamond Rule"
8
class Dprim(B,Cprim):
pass
print Dprim.classe
class maListe(list):
def __init__(self, param):
list.__init__(self, param)
print "ma liste"
>>> x = maListe(range(5))
ma liste
>>> x
[0, 1, 2, 3, 4]
class A(object):
def m(self): "save A’s data"
class B(A):
def m(self): "save B’s data"; super(B, self).m()
class C(A):
def m(self): "save C’s data"; super(C, self).m()
class D(B, C):
def m(self): "save D’s data"; super(D, self).m()
On a alors :
A.__mro__ == (A, object)
B.__mro__ == (B, A, object)
C.__mro__ == (C, A, object)
D.__mro__ == (D, B, C, A, object)
9
super(C, self).m fonctionne ainsi. On commence par trouver la position
de C dans self. class . mro puis on recherche la première classe après C
qui contient la fonction m. Il est recommandé de n’utiliser la classe super que
pour la surcharge de la même fonction (super(MaClasse, self).maMethode
ne doit utilisé que dans la surcharge de maMethode pour une classe fille de
MaClasse).
Que donne le résultat de m pour les quatre classes ? Pourquoi ?
4 Exceptions
Vous avez sûrement déjà rencontré des exceptions lors de vos premiers essais en
python :
print uneVariableNonAffectee # une erreur
La gestion des erreurs est cruciale dans tout programme. Rappelez vous une
des deux règles du zen of Python (PEP20) :
Errors should never pass silently.
Unless explicitly silenced.
Il n’est pas nécessaire de tout vérifier au début de chaque fonction sous peine
d’alourdir considérablement votre programme, mais lorsqu’une erreur survient,
il faut la laisser se propager.
La gestion des erreurs s’effectuent en utilisant les mots clés try et except.
try:
print uneVariableNonAffectee
except:
print "une erreur"
La séquence ci-après vous montre un exemple plus général :
x = []
try:
print x[1]
except IndexError:
print "L’indice n’existe pas"
except (TypeError, NameError):
print "le nom ou le type n’est pas bon"
except:
print "les autres erreurs"
else:
print "tout s’est bien passé"
try:
raise NameError, "C’est farfelu comme nom"
except NameError, inst:
print inst
10
Comprenez son fonctionnement en utilisant l’aide du manuel disponible
ici http://docs.python.org/tutorial/errors.html.
Les exceptions sont des classes comme les autres. La classe de base étant
Exception. On peut l’utiliser pour lever une exception (avec l’aide de la com-
mande raise) ou s’en servir pour créer ses propres exceptions.
try:
raise NameError, "C’est farfelu comme nom"
except Exception, inst:
print inst
try:
raise Exception(’un’, ’deux’, ’et trois zéro’)
except Exception, inst:
print inst
class ErreurDeLaNature(Exception):
def __init__(self, val):
self.commentaire = val
def __str__(self):
return repr(self.commentaire)
try:
raise ErreurDeLaNature("et c’est pas joli à voir")
except ErreurDeLaNature, val:
print "Une erreur de la nature arrive", val
5 Exercice
Les fichiers graph.py et test graphs.py contiennent une implémentation d’une
classe permettant de manipuler les graphes.
Nous considérerons ici :
• que les graphes sont non orientés,
• que chaque arête peut posséder un attribut (un nombre, ou tout autre
objet).
Pour la compréhension de la classe, on pourra lire le lien suivant : http:
//www.python.org/doc/essays/graphs.html.
11
• quelle est l’utilité de la fonction iter ? En quoi diffère-t-elle de la
méthode vertices() ?
• À quoi correspond getitem et comment l’utilise-t-on ?
• Que signifie la commande lambda (retenez cette commande, une fois qu’on
a compris comment ça fonctionne on ne peut s’empêcher de l’utiliser
partout) ?
• Pourquoi avoir défini une classe Sommet vide ?
Vous devriez pouvoir trouver toutes les réponses à vos questions dans les
documentations et liens fournis.
Le module graphs.py définie également un algorithme BellmanFord et une
erreur CircuitError. À l’aide de Wikipédia (par exemple) comprenez leurs
utilités.
• qu’il soit possible de trouver un chemin entre deux nœuds donnée, et que
ce ce chemin soit le plus court possible.On pourra implémenter une ver-
sion non orienté de l’algorithme de Dijkstra (il faut pour cela que tous
les poids des arêtes soient positif, sinon rendre une erreur) que l’on trou-
vera ici http://fr.wikipedia.org/wiki/Algorithme_de_Dijkstra par
exemple.
12