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