您不能選擇超過 %s 個話題 話題必須以字母或數字為開頭,可包含連接號 ('-') 且最長為 35 個字
此存儲庫已封存,您能瀏覽檔案及複製此存儲庫,但不能推送、建立問題及拉取請求。

python-09.md 9.1 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. % Programmation avec Python (chapitre 9)
  2. % Dimitri Merejkowsky
  3. \center \huge Formats et chaînes de caractères
  4. # Formater des chaînes de caractères
  5. Problème:
  6. \vfill
  7. ```python
  8. >>> nom = "Ford"
  9. >>> résultat = 42
  10. >>> message = "Bonjour, " + nom + ". "
  11. >>> message += "La réponse est: " + str(résultat) + "."
  12. >>> message
  13. 'Bonjour, Ford. La réponse est: 42.'
  14. ```
  15. \vfill
  16. Ce n'est pas très lisible ...
  17. 3 solutions différentes en Python
  18. # Solution 1: À l'ancienne
  19. * Avec `%`
  20. * Compatible avec les très vieux Pythons
  21. * Compatibles avec d'autres langage (`printf` en C par example)
  22. # Example 1
  23. ```python
  24. name = "John"
  25. print("Bonjour %s!" % name) # 's' comme string
  26. age = 42
  27. print("Vous avez %i ans" % 42) # 'i' comme integer
  28. ```
  29. # Attention aux types:
  30. ```python
  31. >>> print("Bonjour %i!" % name)
  32. TypeError: %i format: a number is required, not str
  33. ```
  34. # On peut en mettre plusieurs
  35. Avec un tuple:
  36. ```python
  37. print("Bonjour %s, vous avez %i" % (name, age))
  38. ```
  39. \vfill
  40. Avec un dictionnaire:
  41. ```python
  42. data = {"name": "John", "age": 42}
  43. print("Bonjour %(name)s, vous avez %(age)i ans" % data)
  44. ```
  45. # Solution 2: format()
  46. Des `{}` comme "placeholders" et une *méthode* sur les strings:
  47. ```python
  48. >>> nom = "Ford"
  49. >>> résultat = 42
  50. >>> template = "Bonjour, {}. La réponse est: {}"
  51. >>> message = template.format(nom, résultat)
  52. >>> message
  53. 'Bonjour, Ford. La réponse est: 42.'
  54. ```
  55. * Pas de types!
  56. # format() - à plusieurs
  57. On peut aussi nommer les remplacements:
  58. ```python
  59. template = "Bonjour, {nom}. La réponse est: {résultat}"
  60. template.format(nom="Ford", résultat=42)
  61. ```
  62. # format() - à plusieurs
  63. On peut les ordonner:
  64. ```python
  65. template = "Bonjour {1}. La réponse est {0}"
  66. template.format(reponse, name)
  67. ```
  68. * Pas possible avec `%`!
  69. # Solution 3: f-strings
  70. * La meilleure de toutes :)
  71. * Plus succint que `format()`
  72. * Plus performant que `%` et `.format()`
  73. * Mais pas avant Python 3.6
  74. # Principe
  75. On peut mettre du *code* dans `{}` avec la lettre `f` devant la chaîne:
  76. ```python
  77. name = "Patrick"
  78. score = 42
  79. text = f"Bonjour {name}. Votre score est: {score}"
  80. ```
  81. \vfill
  82. Mais aussi:
  83. ```python
  84. text = f"2 + 2 égale { 2 + 2 }"
  85. ```
  86. # Conclusion
  87. Je vous ai présenté `%` et `format()` parce que vous risquez d'en voir.
  88. Mais si vous avez le choix, utilisez des `f-strings`!
  89. # Spécifications de formats
  90. * Permet des opérations pendant la conversion en texte
  91. * Fonctionne avec les 3 solutions
  92. # Tronquer
  93. ```python
  94. >>> pi = 3.14159265359
  95. >>> truncated = "%.2f" % pi
  96. 3.14
  97. ```
  98. Le texte dans les accolades après le `:` est un mini-langage de spécification de format.
  99. `.2` veut dire: 2 chiffres après la virgule maximum.
  100. # Alignements et paddings
  101. On peut aussi faire des alignements et du "padding":
  102. \vfill
  103. ```python
  104. template = "{name:>10}: {score:03}"
  105. print(template.format(name="Alice", score=42))
  106. print(template.format(name="Bob", score=5))
  107. ```
  108. ```
  109. Alice: 042
  110. Bob: 005
  111. ```
  112. * `>10` : aligné à gauche, taille minimum 10
  113. * `03`: rajouter jusqu'à 2 zéros à gauche pour que la taille fasse 3
  114. # Documentation
  115. Documentation ici:
  116. htps://docs.python.org/fr/3/library/string.html#format-specification-mini-language
  117. #
  118. \center \huge Rappels sur les classes
  119. # Classes
  120. ```python
  121. class Car:
  122. total_number_of_cars = 0
  123. def __init__(self, color="black"):
  124. self.color = color
  125. Car.total_number_of_cars += 1
  126. def drive(self):
  127. print("vroom")
  128. @classmethod
  129. def print_number_of_cars(cls):
  130. print(cls.total_number_of_cars,
  131. "cars have been made")
  132. ```
  133. # Composition
  134. ```python
  135. class Authorization:
  136. def __init__(self, credentials_file):
  137. ...
  138. self.password = ...
  139. class Client:
  140. url = "https://exmple.com"
  141. def __init__(self, auth)
  142. self.auth = auth
  143. def make_request(self):
  144. password = self.auth.get_password()
  145. requests.get(url, password=password)
  146. ```
  147. # Héritage - partage des attributs et méthodes
  148. ```python
  149. class A:
  150. def method_in_a(self):
  151. self.attribute_in_a = 42
  152. class B(A):
  153. def method_in_b(self):
  154. self.method_in_a() # ok
  155. self.attribute_in_a # ok
  156. ```
  157. # Héritage - ordre de résolution des méthodes
  158. ```python
  159. class A:
  160. def method_in_a(self):
  161. pass
  162. class B(A):
  163. def method_in_b(self):
  164. pass
  165. >>> a = A()
  166. >>> a.method_in_a() # ok
  167. >>> a.method_in_b() # error
  168. >>> b = B()
  169. >>> b.method_in_b() # ok
  170. >>> b.method_in_a() # ok
  171. ```
  172. # Héritage - ordre de résolution des méthodes
  173. ```python
  174. class A:
  175. def method_in_a(self):
  176. pass
  177. class B(A):
  178. def method_in_b(self):
  179. pass
  180. >>> a = A()
  181. >>> a.method_in_a() # ok
  182. >>> a.method_in_b() # error
  183. >>> b = B()
  184. >>> b.method_in_b() # ok
  185. >>> b.method_in_a() # ok
  186. ```
  187. # Héritage: surcharge
  188. ```python
  189. class A:
  190. def method(self):
  191. print("A!")
  192. class B(A):
  193. def method(self):
  194. print("B!")
  195. >>> a = A()
  196. >>> a.method() # ok
  197. 'A!'
  198. >>> b = B()
  199. >>> b.method()
  200. 'B!'
  201. ```
  202. # Héritage - super()
  203. ```python
  204. class A:
  205. def method(self):
  206. print("A!")
  207. class B(A):
  208. def method(self):
  209. super().method()
  210. print("B!")
  211. >>> a = A()
  212. >>> a.method() # ok
  213. 'A!'
  214. >>> b = B()
  215. >>> b.method()
  216. 'A!'
  217. 'B!'
  218. ```
  219. # Héritage - super() et \_\_init\_\_
  220. ```python
  221. # All animals have a species
  222. class Animal:
  223. def __init__(self, species):
  224. self.species = species
  225. # Pets are animals with a name
  226. class Pet(Animal):
  227. def __init__(self, species, name):
  228. super().__init__(species) # <- à ne pas oublier
  229. self.name = name
  230. # All dogs are pets
  231. class Dog(Pet):
  232. def __init__(self, name):
  233. super().init("dog", name)
  234. ```
  235. #
  236. \center \huge Interfaces et classes abstraites
  237. # Example
  238. Imaginons un jeu où il faut retrouver le nom d'un super-héros à partir
  239. de sa description.
  240. On a une classe `MarvelClient` qui permet de lister les personnages et leurs
  241. descriptions et une class `Game` pour la logique du jeu
  242. # Implémentation - MarvelClient
  243. ```python
  244. class MarvelClient:
  245. url = "https://marvel.com/api"
  246. def __init__(self, credentials_file):
  247. # réupére les clés depuis un fichier
  248. def get_all_characters(self):
  249. # appelle l'api marvel pour récupérer
  250. # tous les personnages
  251. def get_description(self, character_name)
  252. # appelle l'api marvel pour récupérer
  253. # une description
  254. ```
  255. # Implémentation - Game
  256. ```python
  257. import random
  258. class Game:
  259. def __init__(self, marvel_client)
  260. self.auth = marvel_client
  261. def play(self):
  262. characters = self.marvel_client.get_all_characters()
  263. name_to_guess = random.choice(characters)
  264. description = self.marvel_client.get_description(
  265. name_to_guess)
  266. while not self.won():
  267. ...
  268. ```
  269. # Contrats implicites - 1
  270. Il y a un *contrat implicite* entre `Game` et `MarvelClient`.
  271. Dans `play` on appelle `self.marvel_client.get_all_characters()` donc la méthode
  272. `get_all_characters()` doit:
  273. * exister
  274. * ne prendre aucun agrument
  275. * retourner une liste de noms
  276. # Contrats implicites - 2
  277. Pareil avec `get_description()`. La méthode doit:
  278. * exister
  279. * prendre un nom en unique argument
  280. * retourner une description
  281. # Une force et une faiblesse
  282. On peut passer à `Client.__init__()` n'importe qu'elle classe pourvu qu'elle ait
  283. les bonnes méthodes!
  284. On appelle ça "duck typing"
  285. # duck typing
  286. Définition traditionnelle (pas exacte à mon sens):
  287. * Si ça marche comme un canard et que ça fait coin-coin comme un canard alors c'est un canard.
  288. \vfill
  289. Meilleure définition:
  290. * Tu peux passer remplacer le canard par une vache. Tant que la vache a un bec et fait coin-coin, c'est bon!
  291. # En image
  292. ![canard vache](img/canard-vache.jpg)
  293. # Exemple utile
  294. ```python
  295. class FakeClient():
  296. def get_all_characters(self):
  297. return ["Spider-Man", "Batman", "Superman"]
  298. def get_description(self, name):
  299. if name == "Spider-Man":
  300. ...
  301. ...
  302. fake_client = FakeClient()
  303. game = Game(fake_client)
  304. game.play()
  305. # Tout marche!
  306. ```
  307. # Problème
  308. Comment s'assurer que `FakeClient` et `MarvelClient` restent synchronisés?
  309. # Solution
  310. Une classe *abstraite*:
  311. ```python
  312. import abc
  313. class BaseClient(metaclass=abc.ABCMeta):
  314. @abc.abstractmethod
  315. def get_all_characters(self):
  316. pass
  317. @abc.abstractmethod
  318. def get_description(self, name):
  319. pass
  320. >>> client = BaseClient()
  321. ```
  322. On retrouve le `@` au-dessus des méthodes.
  323. On reparlera des metaclasses plus tard :)
  324. # Utilisation
  325. On ne peut pas instancier la classe abstraite directement:
  326. ```python
  327. >>> client = BaseClient()
  328. # Cannot instantiate abstract class BaseClient
  329. # with abstract methods
  330. # get_all_characters, get_description
  331. ```
  332. En revanche on peut en hériter:
  333. ```python
  334. class MarvelClient(BaseClient):
  335. def get_all_characters(self):
  336. ...
  337. def get_description(self, name):
  338. ...
  339. ```
  340. # Pistes de réflexion
  341. * Que se passe-t-il si on rajoute une méthode dans `BaseClient` sans toucher
  342. à `MarvelClient` ni à `FakeClient` ?
  343. * Que se passe-t-il si la signature de `get_description()` change?
  344. # Conclusion
  345. Plein de languages ont un concept d'interface. C'est bien utile de savoir,
  346. les définir en Python, même si ça ne résout pas tous les problèmes.
  347. \center \huge Atelier
  348. Encore un refactoring
  349. # Pour la prochaine fois:
  350. * Créer un compte dévelopeur sur le site de Marvel
  351. * Implémenter le jeu!