Objets et classes

Dans les faits, ce chapitre pourrait faire l’objet d’un atelier d’un week-end à lui tout seul. Nous allons simplement nous concentrer sur les fonctionnalités les plus simples dont vous aurez besoin pour pouvoir travailler avec Django.

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 que “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 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.

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. Prenons par exemple une classe Dog :

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 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.

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. Regardons comment elle fonctionne :

my_new_pet = Dog()
my_new_pet.bark()
Woof! Woof!

Vous pouvez appeler ce premier argument comme vous le souhaitez mais la convention la plus répandue est de l’appeler self.

Attributs des objets

Outre les méthodes (les fonctions définies dans une classe), les objets peuvent également avoir des attributs.

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__ :

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. Nous pouvons maintenant utiliser cet attribut dans les autres méthodes :

class Dog(object):

    def __init__(self, name):
        self.name = name

    def bark(self):
        return "Woof! %s! Woof!" % (self.name,)

snoopy = Dog("Snoopy")
pluto = Dog("Pluto")
print(snoopy.bark())
print(pluto.bark())
Woof! Snoopy! Woof!
Woof! Pluto! 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.

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):
        return "Woof! %s! Woof!" % (self.name,)

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 un object
  • Tout Dog est un Animal, tout Dog est un object

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):
        return "Run %s meters." % (distance,)

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.