@@ -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 | 16-classes-03/index.rst | ||||
17-décorateurs/index.rst | 17-décorateurs/index.rst | ||||
18-exceptions/index.rst | 18-exceptions/index.rst | ||||
19-classes-04/index.rst |