Browse Source

Ajout du chapitre sur les objets

master
Dimitri Merejkowsky 4 years ago
parent
commit
9c793899ec
4 changed files with 266 additions and 0 deletions
  1. +107
    -0
      cours/source/19-classes-04/01-attributs-et-instances-de-classes.rst
  2. +142
    -0
      cours/source/19-classes-04/02-objets.rst
  3. +16
    -0
      cours/source/19-classes-04/index.rst
  4. +1
    -0
      cours/source/index.rst

+ 107
- 0
cours/source/19-classes-04/01-attributs-et-instances-de-classes.rst View File

@@ -0,0 +1,107 @@
Attributs et instances de classe
================================

Rappels
-------

Voici un exemple de classe qui contient une méthode::

class MaClasse
def ma_méthode(self):
print(self.mon_attribut)


Le bloc indenté en-dessous du mot-clé ``class`` s'appelle le
*corps* de la classe. Et les méthodes sont définies avec le
mot-clé ``def`` dans le corps de la classe.

On dit que ce sont des *méthodes d'instance* par ce qu'il
faut créer une instance pour pouvoir les appeler::

mon_instance = MaClasse()
mon_instance.ma_méthode()


Attributs de classes
---------------------

On peut également déclarer des variables dans le corps d'une classe.

On crée ainsi des *attributs de classe*::

class MaClasse:
mon_attribut_de_classe = 42


Ici ``mon_attribut_de_classe`` existe *à la fois* dans les instances de ``MaClasse``
et dans la classe elle-même::

print(MaClasse.mon_attribut_de_classe)
# affiche 42
mon_instance = MaClasse()
print(mon_instance.mon_attribut_de_classe)
# affiche 42


Un point important est que les attributs de classe sont *partagés* entre
toutes les instances. Voici un exemple d'utilisation possible::

class Voiture:
nombre_total_de_voitures_fabriquées = 0

def __init__(self, marque, couleur):
print("Construction d'une", marque, couleur)
Voiture.nombre_total_de_voitures_fabriquées += 1


ferrari_1 = Voiture("Ferrari", "rouge")
mercedes_1 = Voiture("Mercedes, "noire")
ferrari_2 = Voiture("Ferrari", "rouge")
print("total:", Voiture.nombre_total_de_voitures_fabriquées)
# Affiche:
# Construction d'une Ferrari rouge
# Construction d'une Mercedes noire
# Construction d'une Ferrari rouge
# total: 3

Notez que pour changer l'attribut de classe depuis une méthode, (comme dans le méthode
``__init__`` ci-dessus) on utilise le nom de la classe directement, et non pas ``self``.

Méthodes de classes
--------------------

On peut aussi définir des méthodes de classes avec le décorateur `classmethod`

Dans ce cas, le permier argument s'appelle ``cls`` et prend la valeur de la *classe*
elle-même. Pour poursuivre sur notre exemple::

class Voiture:
nombre_total_de_voitures_fabriquées = 0

def __init__(self, marque, couleur):
print("Construction d'une", marque, couleur)
Voiture.nombre_total_de_voitures_fabriquées += 1

@classmethod
def fabrique_ferrari(cls):
return cls("ferrari", "rouge")


ferrari = Voiture.fabrique_ferrari()


Détaillons ce qu'il se passe sur la dernière ligne:
à gauche du égal il y a une variable et à droite une expression(``Voiture.fabrique_ferrari()``)

L'expression est constitué d'une classe à gauche du point (``Voiture``) et
d'un attribut à droite du point ``fabrique_ferrari`` suivi de parenthèses.

Comme ``fabrique_ferrari`` est une méthode de classe, on va appeler la méthode
de classe ``fabrique_ferrari`` en lui passent la classe Courante en argument.

On arrive ainsi dans le corps de la méthode de classe ``fabrique_ferrari``, et
``cls`` vaut la classe `Voiture`.

Finalement, on évalue l'expression ``cls("ferrari", rouge")`` en remplaçant
``cls`` par sa valeur, ce qui donne ``Voiture("ferrari", "rouge")`` qui
correspond bien à ce qu'on obtient : une instance de la classe Voiture.

+ 142
- 0
cours/source/19-classes-04/02-objets.rst View File

@@ -0,0 +1,142 @@
Objets
======


En fait, *tout ce qu'on manipule en Python* est un objet. Et tous les objets sont
toujours des instances d'une classe - on peut accéder à la classe qui a servi
à instancier un objet avec la fonction ``type``, par exemple::

class MaClasse:
pass

mon_instance = MaClasse()
print(type(mon_instance))
# Affiche:
# <class '__main__.MaClasse'>


Mais aussi::

print(type(2))
# affice: int

print(type("bonjour"))
# affice: str

Donc en Python, les entiers sont des instances de la classe ``int``, et les strings des instances de la
classe ``str``.

Ainsi, vous pouvez voir l'expression ``x = str(y)`` de deux façons:

* Soit on appelle la fonction native ``str`` pour convertir ``y`` en string

* Soit on crée une nouvelle instance de la classe ``str`` en appelant le constructeur
de la classe ``str`` avec ``y`` en argument.

Notez que ça ne s'arrète pas là::

def ma_fonction():
pass

print(type(ma_fonction))
# affiche: function

class MaClasse:
def ma_méthode(self):
pass

mon_instance = MaClasse()
print(type(mon_instance.ma_méthode))
# affiche: method

import sys
print(type(sys))
# affiche: module


Et même::

print(type(MaClasse))
# affiche: type

print(type(type))
# affiche: type

Et oui, les classes elles-mêmes sont des instances de classe! (la classe ``type``)

Du coup en Python, le terme 'objet' désigne *toujours* une instance de classe - même
``None`` est une instance d'une classe (elle s'appelle ``NoneType``).

Ordre de résolution
--------------------

Il est temps de revenir sur l'évaluation des expressions impliquant des
attributs.

On a vu trois systèmes différents:

Appeler une fonction définie dans un module::

import mon_module
mon_module.ma_fonction()

Appeler une méthode d'instance définie dans une classe::

mon_instance = MaClasse()
mon_instance.ma_méthode()

Appeler une méthode de classe définie dans une classe::

MaClasse.ma_méthod_de_classe()

D'après ce qu'on a vu dans la section précédente, on sait maintenant que
dans tous les cas, à gauche du point se situe un objet, et que tous
les objets sont des instances d'une classe (appelé le "type" de l'objet).

Pour évaluer l'expression ``mon_objet.mon_attribut``, où `mon_objet`` est une
instance de ``mon_type``, Python cherche toujours l'attribut dans deux endroits:

* D'abord en tant qu'attribut de l'instance ``mon_objet``
* Ensuite, en tant qu'attribut de la classe ``mon_type``

La recherche se poursuit ainsi en suivant toutes les classe parentes de
``mon_type``.


On peut voir ce mécanisme en action dans le code suivant::

class A:
def f1(self):
print("f1 dans A")

def f2(self):
print("f2")


class B(A):
@classmethod
def f3(cls):
print("f3")

def f1(self):
print("f1 dans B")


b = B()
b.f1()
b.f3()
b.f2()
# affiche:
# f1 dans B
# f3
# f2


Conclusion
------------

Maintenant vous devriez comprendre pourquoi on dit parfois qu'en
Python, **tout est objet**.

Dans un prochain chapitre, on expliquera pourquoi en plus de cela
on peut dire qu'en Python, **tout est dictionnaire**.

+ 16
- 0
cours/source/19-classes-04/index.rst View File

@@ -0,0 +1,16 @@
Objets
======

On a déjà parlé de *programmation* orientée objet en Python, mais pour l'instant
on a vu que des classes et des instances.

Or le concept d'objet existe bel et bien en Python, et il est plus que temps
de vous le présenter.

Mais avant, il faut faire un petit détour par les attributs et méthodes de classes.

.. toctree::
:maxdepth: 1

./01-attributs-et-instances-de-classes.rst
./02-objets.rst

+ 1
- 0
cours/source/index.rst View File

@@ -35,3 +35,4 @@ remarques.
16-classes-03/index.rst
17-décorateurs/index.rst
18-exceptions/index.rst
19-classes-04/index.rst