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