@@ -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. |
@@ -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**. |
@@ -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 |
@@ -35,3 +35,4 @@ remarques. | |||
16-classes-03/index.rst | |||
17-décorateurs/index.rst | |||
18-exceptions/index.rst | |||
19-classes-04/index.rst |