% Programmation avec Python (chapitre 6) % Dimitri Merejkowsky
\center \huge Orienté objet et classes
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
>>> id(c)
140400601470800
Notez bien les deux objets différents (le fait que l’objet pointé ait la même valeur n’a pas d’importance)
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)
Aujourd’hui on va parler de l’orienté objet, OOP en anglais (ou juste OO)
Un “objet” informatique représente un véritable “objet” physique dans le vrai monde véritable.
Ce n’est pas une très bonne définition:
Je le mentionne juste parce que c’est une idée reçue très répandue.
Mettre au même endroit:
L’important c’est que les deux aillent ensemble
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
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!
La seule chose dont on a besoin pour construire un objet, c’est un plan.
On note le plan avec le mot-clé class
et un nom:
class MyObject:
pass
La classe est le plan de construction de notre objet.
>>> object_1 = MyObject()
Python ne sait rien de l’objet à part son adresse on mémoire et son nom:
>>> id(object_1)
139993522758320
>>> print(object_1)
<__main__.MyObject at 0x7f52c831e2b0>
On appelle object_1
une instance de la classe MyObject
.
>>> object_2 = MyObject()
>>> id(object_2)
140409432622920
>>> print(object_2)
<__main__.MyObject at 0x7fb39e5ac748>
Une autre adresse mémoire, donc un objet différent.
Une fonction dans une classe:
class MyObject:
def my_method(the_object):
print("hello", the_object)
\vfill
C’est tout!
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>
>>> object = MyObject()
>>> object
<MyObject at 0x7f52c9f6d6d8>
>>>> 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>
Il faut passer l’objet en cours en paramètre:
class MyObject:
def broken():
print("You cannot call me!")
>>> o = MyObject()
>>> o.broken()
TypeError: broken() takes 0 positional arguments
but 1 was given
self
class MyObject:
def my_method(self):
print("hello", self)
>>> object = MyObject()
>>> object.attribute # l'attribut n'existe pas
AttributError
>>> object.attribute = 42 # maintenant oui
>>> object.attribute
42
Avec self
, bien sûr:
class MyObject:
def print_attribute(self):
print(self.attribute)
def change_attribute(self, new_value)
self.attribute = new_value
>>> object = MyObject()
>>> object.print_attribute() # l'attribut n'existe pas
AttributError
>>> object.attribute = 42
>>> object.print_attribute() # maintenant oui
42
>>> object.change_attribute(43)
>>> object.attribute
43
Avec __init__
:
class MyObject:
def __init__(self):
self.attribute = 42
>>> object = MyObject()
>>> object.attribute
42
__init__()
et MyObject()
sont des appels de fonctions comme les autres
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"
En vrai, on nomme souvent les paramètres du constructeur et les attributes de la même façon.
class Car:
def __init__(self, color="black"):
self.color = color
self
en premier argument)\center \huge Modules
Et oui, vous faites des modules sans le savoir depuis le début :)
Un fichier foo.py
correspond au module foo
C’est pas tout à fait réciproque. Le module foo
peut venir d’autre chose
qu’un fichier.
Ou: accéder à du code provenant d’un autre fichier source.
# Dans foo.py
a = 42
\vfill
# Dans bar.py
import foo
print(foo.a)
\vfill
On dit aussi namespace.
Du point de vue de bar.py
, a
est dans l’espace de nom foo.
On retrouve la syntaxe pour accèder à un attribut: <variable>.<membre>
.
(ce n’est pas un hasard)
Les namespaces sont automatiques en Python
# dans foo.py
def ma_fonction():
return 42
# dans le REPL
>>> import foo
>>> foo.ma_fonction()
42
print()
à la fin de foo.py
;)# Dans foo.py
print("Je suis le module foo et tu viens de m’importer")
>>> import foo
Je suis le module foo et tu viens de m’importer
>>> import foo
<rien>
On retrouve le concept de cache.
Il faudra donc redémarrer le REPL à chaque fois que le code change.
Parfois, les gens conseillent d’utiliser reload()
mais cette fonction n’est pas toujours fiable :/
# Dans foo.py
a = 42
def ma_fonction():
return 43
\vfill
# Dans bar.py
from foo import ma_fonction()
ma_fonction()
>>> from foo import *
>>> ma_fonction()
42
Un script, par opposition à un module n’est pas censé être importé.
Une solution est de mettre des tirets dans le nom:
# Dans foo.py
import my-script
script
à my
La méthode main()
ne doit pas être exécutée quand on importe le code!
Solution:
# Dans foo.py
def my_function():
# Fonction utile qui peut être ré-utilisée
def main():
# Fonction d'entrée principale
# Utilise my_function()
# magie!
if __name__ == "__main__":
main()
Le if
n’est vrai que quand on lance python3 foo.py
, mais pas quand on appelle import foo
depuis
un autre module.
La variable magique __name__
est égale au nom du module quand il est importé, et à la string "__main__"
sinon.
sys
, pour sys.exit()
ou sys.argv
Toute la librarie standard est documentée - même en Français.
https://docs.python.org/fr/3/library/index.html
Beacoup de choses dedans. (batteries included)
De quoi faire de la manipulation de texte, des statistiques, du réseau, de la concurrence, etc ...
\center \huge Atelier
API web: un serveur avec qui on peut parler depuis un programme.
Il en existe des quantités sur internet.
Aujourd’hui on va utiliser numbersapi.com
Example: On fait une requête sur http://numbersapi.com/42
, on récupère du texte
contenant un fait intéressant (trivia en anglais) à propos du nombre 42 .
Voir sur GitHub: https://github.com/E2L/cours-python/blob/master/sources/numbers/numbers_proc.py
import sys
import urllib.request
def main():
number = sys.argv[1]
with urllib.request.urlopen("http://numbersapi.com/" + number) as request:
response = request.read().decode("utf-8")
print(response)
if __name__ == "__main__":
main()
Démo faite en cours.
avant:
with urllib.request.urlopen(
"http://numbersapi.com/" + number) as request:
...
après:
url = "http://numbersapi.com/" + number
with urllib.request.urlopen(url) as request:
def get_trivia(self, number):
url = "http://numbersapi.com/" + number
with urllib.request.urlopen(url) as request:
response = request.read().decode("utf-8")
return response
def build_url(self, number):
return "http://numbersapi.com/" + number
def do_request(self, url):
with urllib.request.urlopen(url) as request:
response = request.read().decode("utf-8")
return response
def get_trivia(self, number):
url = self.build_url(number)
return self.do_request(url)
Voir sur GitHub: https://github.com/E2L/cours-python/blob/master/sources/numbers/numbers_object.py
Partir des sources dans le répertoire hangman
:
https://github.com/E2L/cours-python/tree/master/sources/hangman
Rajouter la gestion des scores (dans scores.py
) au code exsistant
du jeu du pendu (dans hangman.py
)
Refactorer en essayant d’introduire des classes
Partir du code dans numbers_object.py
, rajouter la getsion
des autres URL de http://numbersapi.com
Envoyez-moi un e-mail à d.merej@gmail.com
:)