| @@ -0,0 +1,252 @@ | |||||
| % Programmation avec Python (chapitre 6) | |||||
| % Dimitri Merejkowsky | |||||
| # | |||||
| \center \huge Les classes | |||||
| # Paradigmes | |||||
| Une façon d'envisager le code. | |||||
| Pour l'instant on n'a vu que le paradigme *procédural* (ou (impératif). | |||||
| Il y en a plein d'autres! (*fonctionnel* notamment, dont on parlera un jour) | |||||
| # Détails du procédural | |||||
| * des types simples (entiers, booléens) | |||||
| * des structures de données (dictionnaires, listes ...) | |||||
| * des fonctions qui manipulent des types simples ou des structures | |||||
| * les fonctions sont appelées les unes après les autres | |||||
| Aujourd'hui on va parler de *l'orienté objet*. | |||||
| # Un petit détour | |||||
| Un nouveau built-in: `id()` | |||||
| L'adresse de l'objet pointé par la variable: | |||||
| ``` | |||||
| >>> a = 42532 | |||||
| >>> id(a) | |||||
| 94295009035968 | |||||
| >>> b = a | |||||
| >>> id(b) # même objet | |||||
| 94295009035968 | |||||
| >>> c = 42532 # objet différent, même valeur | |||||
| >>> id(c) | |||||
| ``` | |||||
| # Orienté objet - 1ère définition | |||||
| Mettre au même endroit: | |||||
| * des données | |||||
| * des fonctions qui opèrent sur ces données | |||||
| L'important c'est que les deux aillent ensemble | |||||
| OOP en Anglais (ou juste OO) | |||||
| # Orienté objet - 2ème définition | |||||
| Des "cellules" qui s'envoient des "messages". | |||||
| Notamment, les cellules ne "voient" que leur état interne. | |||||
| On peut envoyer un message d'une cellule à une autre *sans* connaître | |||||
| beaucoup de détails à propos du destinataire du message | |||||
| # Les classes | |||||
| On va parler *d'une* façon de faire de l'orienté objet: avec des classes. | |||||
| Mais notez bien qu'on peut faire de l'orienté objet *sans* classes! | |||||
| # Le plan de construction | |||||
| La seule chose dont on a besoin, c'est le mot-clé `class` et un nom. | |||||
| ```python | |||||
| class MyObject: | |||||
| pas | |||||
| ``` | |||||
| La classe est le plan de construction de notre objet. | |||||
| # Créons des objets | |||||
| ```python | |||||
| >>> object_1 = MyObject() | |||||
| >>> object_2 = MyObject() | |||||
| ``` | |||||
| Python ne sait rien de l'objet à part son adresse on mémoire et son nom: | |||||
| ```python | |||||
| print(object_1) | |||||
| <__main__.MyObject at 0x7f52c831e2b0> | |||||
| ``` | |||||
| On appelle `object_1` et `object_2` des *instances* de la classe `MyObject`. | |||||
| # Méthodes | |||||
| Une fonction dans une classe | |||||
| ``` | |||||
| class MyObject: | |||||
| def my_method(the_object): | |||||
| print("hello", the_object) | |||||
| ``` | |||||
| C'est tout! | |||||
| # Méthodes - 2 | |||||
| La méthode n'existe pas en dehors de la classe (souvenez vous des cellules !) | |||||
| ``` | |||||
| >>> my_method() | |||||
| NameError | |||||
| >>> object = MyObject() | |||||
| >>> object.my_method() | |||||
| Hello, <MyObject at 0x7f52c9f6d6d8> | |||||
| ``` | |||||
| Notez que `my_method` a pris en premier argument ce qu'il y avait *à gauche* du point: | |||||
| D'ailleurs, ce code fonctionne aussi et retourne *la même chose*: | |||||
| ``` | |||||
| >>> MyObject.my_method(object) | |||||
| Hello, <MyObject at 0x7f52c9f6d6d8> | |||||
| ``` | |||||
| # Méthodes - 3 | |||||
| Il *faut* passer l'objet en cours en paramètre: | |||||
| ```python | |||||
| class MyObject: | |||||
| def broken(): | |||||
| print("You cannot call me!") | |||||
| ``` | |||||
| ```python | |||||
| >>> o = MyObject() | |||||
| >>> o.broken() | |||||
| TypeError: broken() takes 0 positional arguments but 1 was given | |||||
| ``` | |||||
| # Conventions | |||||
| * Les classes sont en CamelCase | |||||
| * Tout le reste (méthodes, etc...) en snake_case | |||||
| * L'objet en cours s'appelle **toujours** `self` | |||||
| ```python | |||||
| class MyObject: | |||||
| def my_method(self): | |||||
| print("hello", self) | |||||
| ``` | |||||
| # Attributs | |||||
| * Des variables dans un objet. | |||||
| * On peut ajouter un attribut quand on veut à qui on veut, et toujours avec le | |||||
| point au milieu: | |||||
| ```python | |||||
| >>> object = MyObject() | |||||
| >>> object.attribute # ici l'attribut n'existe pas | |||||
| AttributError | |||||
| >>> object.attribute = 42 # maintenant oui | |||||
| >>> object.attribute | |||||
| 42 | |||||
| ``` | |||||
| # Attributs dans les méthodes | |||||
| Avec `self`, bien sûr: | |||||
| ```python | |||||
| class MyObject: | |||||
| def print_attribute(self): | |||||
| print(self.attribute) | |||||
| def change_attribute(self, new_value) | |||||
| self.attribute = new_value | |||||
| ``` | |||||
| # Accéder aux attributs | |||||
| ```python | |||||
| >>> object = MyObject() | |||||
| >>> object.print_attribute() # ici l'attribut n'existe pas | |||||
| AttributError | |||||
| >>> object.attribute = 42 | |||||
| >>> object.print_attribute() # ça marche | |||||
| 42 | |||||
| >>> object.change_attribute(43) | |||||
| >>> object.attribute | |||||
| 43 | |||||
| ``` | |||||
| # Initialisation des attributs | |||||
| Avec `__init__`: | |||||
| * méthode "spéciale" | |||||
| * appelée automatiquement | |||||
| * notez les deux underscores avant et après ('dunder' en Anglais) | |||||
| ```python | |||||
| class MyObject: | |||||
| def __init__(self): | |||||
| self.attribute = 42 | |||||
| ``` | |||||
| ```python | |||||
| >>> object = MyObject() | |||||
| >>> object.attribute | |||||
| 42 | |||||
| ``` | |||||
| # Construire des objets différents | |||||
| `__init__()` et `MyObject()` sont des appels de fonctions comme les autres | |||||
| ```python | |||||
| class Car: | |||||
| def __init__(self, color_to_use="black"): | |||||
| self.color = color_to_use | |||||
| >>> ford = Car() | |||||
| >>> ford.color | |||||
| "black" | |||||
| >>> ferrari = Car(color_to_use="red") | |||||
| >>> ferrari.color | |||||
| "red" | |||||
| ``` | |||||
| # Notes | |||||
| En vrai, on nomme souvent les paramètres du constructeur et les attributes de la même façon. | |||||
| ```python | |||||
| class Car: | |||||
| def __init__(self, color="black"): | |||||
| self.color = color | |||||
| ``` | |||||
| # Récapitulatif | |||||
| * Classe: plan de construction | |||||
| * Object: ce qu'on crée avec le plan | |||||
| * Instance: objet issue d'une classe | |||||
| * Méthode: fonction dans une classe (qui prend `self` en premier argument) | |||||
| * Attribut: variable dans un objet | |||||