Objets et classes
Dans les faits, ce chapitre pourrait faire l'objet d'un cours complet. Nous allons simplement nous concentrer sur les fonctionnalités les plus simples dont vous aurez besoin pour pouvoir travailler avec python. En effet, on peut distinguer deux approches de l'objet en python :
- Le côté utilisateur, il faut savoir utiliser les objets
- Le côté développeur, pour la création de nouveaux objets
Nous allons nous focaliser sur le premier point.
Vous pouvez trouver une version jupyter notebook de cette partie sur l'espace de cours elearn.
Toute valeur est un objet
Toutes les choses que nous avons appelées valeur jusqu'à présent peuvent être appelé "un objet" dans l'univers de Python. On dit souvent qu'en Python "tout est objet".
Par exemple les entiers, pour lesquels la fonction help() nous retournait des
dizaines de lignes d'information à propos de int()
sont aussi des objets.
Tout objet a une classe
Une classe est le type d'un objet (un type d'objet ?). Par analogie, on peut dire que c'est le moule qui permet de créer l'objet.
On peut tout simplement utiliser la fonction
type()
pour connaitre le type d'un objet :
>>> type(2)
<class 'int'>
>>> type(2.0)
<class 'float'>
>>> type("spam eggs")
<class 'str'>
>>> x = 1, 2
>>> type(x)
<class 'tuple'>
>>> type([])
<class 'list'>
Nous avons déjà parlé des classes que vous pouvez voir ici : int
, float
,
str
, tuple
.
Quand nous utilisons des nombres dans notre programme, nous attendons qu'ils se comportent comme des nombres, et nous savons intuitivement ce qu'est un nombre. Par contre, Python doit savoir exactement ce que signifie "être un nombre".
Par exemple que se passe-t-il lorsqu'on additionne deux nombres ? Ou qu'on les
divise ? La classe int
définit tout cela et bien plus.
En utilisant la fonction help()
, vérifiez ce que nous donne la classe str
.
Voici quelques fonctionnalités intéressantes :
>>> help(str.lower)
Help on method_descriptor:
lower(...)
S.lower() -> str
Return a copy of the string S converted to lowercase.
>>> help(str.upper)
Help on method_descriptor:
upper(...)
S.upper() -> str
Return a copy of S converted to uppercase.
>>> help(str.ljust)
Help on method_descriptor:
ljust(...)
S.ljust(width[, fillchar]) -> str
Return S left-justified in a Unicode string of length width. Padding is
done using the specified fill character (default is a space).
>>> help(str.center)
Help on method_descriptor:
center(...)
S.center(width[, fillchar]) -> str
Return S centered in a string of length width. Padding is
done using the specified fill character (default is a space)
Toutes ces opérations (ou méthodes) sont applicable à n'importe quelle chaîne de caractères. Pour y accéder, on ajoute un point suivi de l'appel de la fonction à appliquer :
>>> x = "Ala"
>>> x.upper()
'ALA'
>>> x.lower()
'ala'
>>> x.center(9)
' Ala '
Une fonction appliquée à un objet est appelée une méthode de l'objet. Il est important de comprendre le rôle du point. Dans ces situations, on lit de droite à gauche :
- méthode
upper
appliquée à x - méthode
lower
appliquée à x - méthode
center
appliquée à x
Le point sépare en quelque sorte un contenant et un contenu. On applique la
méthode upper
contenue dans la classe str
.
Encore une dernière chose importante, pour créer un nouvel objet, on appelle la classe de l'objet (dans le jargon technique on dit qu'on instancie un objet). L'objet ainsi créé est appelé une instance de la classe :
>>> int()
0
>>> str()
''
>>> list()
[]
>>> tuple()
()
Une instance est donc une nouvelle valeur du type décrit par la classe.
Pour résumer, nous avons vu les classes int()
, str()
, tuple()
et list()
.
Nous avons vu que pour connaitre la classe décrivant une valeur (un objet),
nous pouvions regarder son type avec la fonction type()
. Pour créer une
instance de la classe (un nouvel objet), on appelle la classe de la même manière
que nous appelons une fonction, en ajoutant des parenthèses (). Par exemple :
int()
.
Définir une classe
Les classes telles que int
ou str
font partie du langage Python et sont
déjà définies, mais nous pouvons créer nos propres classes pour définir leur
comportement. Cela s'appelle définir une classe.
Il est aussi facile de définir une classe que de définir une fonction. En fait
une classe n'est rien de plus qu'un ensemble de fonctions, appelées méthodes.
Prenons par exemple une classe Dog
qui contient une méthode bark
:
class Dog(object):
def bark(self):
print("Woof! Woof!")
Les classes commencent par le mot clé class
, suivi du nom de la classe.
L'(object)
indique que le nouveau type Dog
est un nouveau type de l'ensemble
des classes de type object
. Ainsi, les instances de notre classe, c'est à dire
les objets créés, seront de type Dog
mais également du type plus général des
objects
. Pour les habitués de la programmation orientée objet, cela signifie
que la classe Dog
hérite de la classe object
, mais laissons cela de côté
pour l'instant.
En fait c'est exactement pour cela qu'on dit que "tout est objet en Python". Car chaque classe est une spécialisation de la classe object de Python. C'est pourquoi quasiment chaque valeur est de type général object.
Il est important de noter que chaque fonction d'une classe doit prendre pour
premier argument la valeur de l'objet duquel elle a été appelée. Nous l'appelons systématiquement self
par convention. Dans notre exemple, nous avons une
fonction appelée bark
("aboyer" en anglais), qui comme vous le voyez n'a qu'un
seul argument, self
. Regardons comment elle fonctionne :
>>> my_new_pet = Dog()
>>> my_new_pet.bark()
Woof! Woof!
Attributs des objets
Outre les méthodes (les fonctions définies dans une classe), les objets peuvent également avoir des attributs. Par exemple :
my_new_pet = Dog()
my_new_pet.name = "Snoopy"
print(my_new_pet.name)
Snoopy
Parfois nous souhaitons que tous les objets d'une classe aient un attribut, par
exemple tous les chiens doivent avoir un nom. Nous pouvons le spécifier en
créant une fonction, au nom spécial, appelée __init__()
. Par
exemple, attribuer un nom à notre chien :
class Dog(object):
def __init__(self, name):
self.name = name
def bark(self):
print("Woof! Woof!")
Dans la fonction __init__()
, nous avons assigné une valeur à un nouvel
attribut name
de l'objet self
. Comme expliqué précédemment, self
est
l'objet courant de la classe Dog
que nous sommes en train de manipuler.
Remarque : On retrouve le point qui comme pour les méthodes indique que
l'attribut name
est un attribut de self
(le nom est contenu dans la classe).
Nous pouvons maintenant utiliser cet attribut dans les autres méthodes :
class Dog(object):
def __init__(self, name):
self.name = name
def bark(self):
print(self.name, " Woof! Woof!")
snoopy = Dog("Snoopy")
pluto = Dog("Pluto")
print(snoopy.bark())
print(pluto.bark())
Snoopy Woof! Woof!
Pluto Woof! Woof!
La fonction __init__()
est appelée durant la création de l'objet. On l'appelle
constructeur, car elle aide à la création de l'objet et lui donne un état de
départ.
Dans cet exemple, la fonction __init__()
accepte deux arguments: self
et
name
, mais quand on créé une instance de la classe Dog
, nous ne spécifions
que l'argument name
, self
est automatiquement spécifié par Python. Désormais,
lorsque que nous instancions un nouvel objet Dog
, celui-ci a un attribut :
son nom.
Héritage
Dans le chapitre précédent, nous avons créé une classe Dog comme sous-ensemble du type object, mais ce n'est pas la seule possibilité. Nous pouvons également dire que Dog est aussi un Animal :
class Animal(object):
pass
class Dog(Animal):
def __init__(self, name):
self.name = name
def bark(self):
print(self.name, " Woof! Woof!")
Nous avons donc une nouvelle classe Animal
, qui hérite du type object
.
Dog
hérite du type Animal
. En d'autres termes :
- Tout
Animal
est unobject
- Tout
Dog
est unAnimal
, toutDog
est unobject
Ainsi nous pouvons décrire des comportements communs à tous les Animaux dans
notre classe Animal
, par exemple le fait de courir, et laisser dans la classe
Dog
des comportements plus spécifiques, comme aboyer:
class Animal(object):
def run(self, distance):
print("Run ", distance, " meters.")
La méthode run
sera disponible pour tous les sous-types de Animal
(comme
les objets de type Dog
par exemple) :
>>> scooby = Dog("Scooby")
>>> print(scooby.run(10))
Run 10 meters.
Arbre de noël
Revenons à l'arbre de noël que nous avons vu au chapitre précédent.
Écrire une classe XMASTree qui pour une taille donnée et lors de l'appel de
la méthode draw()
va afficher les résultats suivants (pour les tailles 1, 2
et 3) :
*
/|\
/_|_\
|
*
/|\
/_|_\
/|\
/ | \
/__|__\
|
*
/|\
/_|_\
/|\
/ | \
/__|__\
/|\
/ | \
/ | \
/___|___\
|