Coverage for src/ensae_teaching_cs/faq/faq_python.py: 100%

52 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-01-27 05:44 +0100

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Quelques questions d'ordre général autour du langage Python. 

5 

6""" 

7 

8import os 

9import io 

10import re 

11import urllib.request 

12 

13 

14def entier_grande_taille(): 

15 """ 

16 

17 .. faqref:: 

18 :tag: python 

19 :title: Quel est l'entier le plus grand ? 

20 

21 La version 3 du langage Python a supprimé la constante ``sys.maxint`` 

22 qui définissait l'entier le plus grand (voir 

23 `What's New In Python 3.0 <https://docs.python.org/3.1/whatsnew/3.0.html#integers>`_). 

24 De ce fait la fonction `getrandbit <https://docs.python.org/3/library/random.html#random.getrandbits>`_ 

25 retourne un entier aussi grand que l'on veut. 

26 

27 .. runpython:: 

28 :showcode: 

29 

30 import random,sys 

31 x = random.getrandbits(2048) 

32 print(type(x), x) 

33 

34 Les calculs en nombre réels se font toujours avec huit octets de précision. 

35 Au delà, il faut utiliser la librairie `gmpy2 <http://gmpy2.readthedocs.org/en/latest/>`_. 

36 Il est également recommandé d'utiliser cette librairie pour les grands nombres entiers 

37 (entre 20 et 40 chiffres). La librairie est plus rapide que l'implémentation 

38 du langage Python (voir `Overview of gmpy2 <https://gmpy2.readthedocs.org/en/latest/overview.html>`_). 

39 

40 .. faqref:: 

41 :tag: python 

42 :title: Tabulations ou espace ? 

43 

44 Il est préférable de ne pas utiliser les tabulations et de les remplacer par des espaces. 

45 Lorsqu'on passe d'un Editeur à un autre, les espaces ne bougent pas. Les tabulations sont plus ou moins grandes visuellement. 

46 L'essentiel est de ne pas mélanger. 

47 Dans `SciTE <http://www.scintilla.org/SciTE.html>`_, il faut aller dans le menu Options / Change Indentation Settings... 

48 Tous les éditeurs ont une option similaire. 

49 """ 

50 pass 

51 

52 

53def difference_div(): 

54 """ 

55 .. faqref:: 

56 :tag: python 

57 :title: Quelle est la différence entre / et // - division ? 

58 

59 Le résultat de la division avec l'opérateur ``/`` est toujours réel : 

60 la division de deux entiers ``1/2`` donne ``0.5``. 

61 Le résultat de la division avec l'opérateur ``//`` est toujours entier. 

62 Il correspond au quotient de la division. 

63 

64 :: 

65 

66 div1 = 1/2 

67 div2 = 4/2 

68 div3 = 1//2 

69 div4 = 1.0//2.0 

70 print(div1,div2,div3,div4) # affiche (0.5, 2.0, 0, 0) 

71 

72 Le reste d'une division entière est obtenue avec l'opérateur ``%``. 

73 

74 :: 

75 

76 print ( 5 % 2 ) # affiche 1 

77 

78 C'est uniquement vrai pour les version Python 3.x. 

79 Pour les versions 2.x, les opérateurs ``/`` et ``//`` avaient des comportements différents 

80 (voir `What’s New In Python 3.0 <https://docs.python.org/3/whatsnew/3.0.html#integers>`_). 

81 """ 

82 div1 = 1 / 2 

83 div2 = 4 / 2 

84 div3 = 1 // 2 

85 div4 = 1.0 // 2.0 

86 return div1, div2, div3, div4 

87 

88 

89def python_path(): 

90 """ 

91 .. faqref:: 

92 :tag: python 

93 :title: Comment éviter sys.path.append... quand on développe un module ? 

94 

95 Lorsqu'on développe un module, 

96 on ne veut pas l'installer. On ne veut pas qu'il soit présent dans le répertoire ``site-packages`` de la distribution 

97 de Python car cela introduit deux versions : celle qu'on développe et celle qu'on a installer. 

98 Avant, je faisais cela pour créer un petit programme utilisant mon propre module 

99 (et on en trouve quelque trace dans mon code) : 

100 

101 :: 

102 

103 import sys 

104 sys.path.append("c:/moncode/monmodule/src") 

105 import monmodule 

106 

107 Quand je récupère un programme utilisant ce module, il me faudrait ajouter 

108 ces petites lignes à chaque fois et c'est barbant. 

109 Pour éviter cela, il est possible de dire à l'interpréteur Python d'aller chercher 

110 ailleurs pour trouver des modules en ajoutant le chemin à la 

111 `variable d'environnement <http://fr.wikipedia.org/wiki/Variable_d'environnement>`_ 

112 `PYTHONPATH <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH>`_. 

113 Sous Windows : 

114 

115 :: 

116 

117 set PYTHON_PATH=%PYTHON_PATH%;c:\\moncode\\monmodule\\src 

118 """ 

119 return os.environ.get("PYTHON_PATH", "") 

120 

121 

122def test_unitaire(): 

123 """ 

124 .. faqref:: 

125 :tag: python 

126 :title: Qu'est-ce qu'un test unitaire ? 

127 :lid: faq-python-tu 

128 

129 Un `test unitaire <http://fr.wikipedia.org/wiki/Test_unitaire>`_ 

130 une procédure permettant de vérifier le bon fonctionnement d'une partie précise. 

131 Concrètement, cela consiste à écrire une fonction qui exécute la partie de code 

132 à vérifier. Cette fonction retourne ``True`` si le test est valide, c'est-à-dire 

133 que la partie de code s'est comportée comme prévue : elle a retourné le résultat attendu. 

134 Elle déclenche une exception si elle le code à vérifier ne se comporte pas comme prévu. 

135 

136 Par example, si on voulait écrire un test unitaire pour la fonction 

137 `pow <https://docs.python.org/3/library/functions.html#pow>`_, on pourrait 

138 écrire :: 

139 

140 def test_pow(): 

141 assert pow(2,1) == 2 # on vérifie que 2^1 == 0 

142 assert pow(2,0) == 1 

143 assert pow(2,-1) == 0.5 

144 assert pow(2,-1) == 0.5 

145 assert pow(0,0) == 1 # convention, on s'assure qu'elle ne change pas 

146 assert isinstance(pow(-2,3.4), complex) 

147 return True 

148 

149 

150 **A quoi ça sert ?** 

151 

152 On écrit la fonction ``x_exp`` (:math:`=y x^{-n}`) comme suit :: 

153 

154 def x_exp(x,y,n): 

155 return y / pow(x,n) 

156 

157 La fonction retourne 0 si :math:`x=y=n=0`. 

158 Admettons maintenant qu'un dévelopeur veuille changer la convention :math:`0^0=1` en :math:`0^0=0`. 

159 La fonction précédente produira une erreur à cause d'une division ``0/0``. 

160 Un test unitaire détectera au plus tôt cette erreur. 

161 

162 Les tests unitaires garantissent la qualité d'un logiciel qui est considéré comme bonne 

163 si 80% du code est couvert par un test unitaire. Lorsque plusieurs personnes 

164 travaillent sur un même programme, un dévelopeur utilisera une fonction faite par un 

165 autre. Il s'attend donc à ce que la fonction produise les mêmes résultats 

166 avec les mêmes entrées 

167 **même si on la modifie ultérieurement**. 

168 Les tests unitaires servent à s'assurer qu'il n'y a pas d'erreur 

169 introduite au sein des résultats intermédiaire d'une chaîne de traitement, auquel 

170 cas, c'est une cascade d'erreur qui est susceptible de se produite. La source 

171 d'une erreur est plus facile à trouver lorsque les tests unitaires sont nombreux. 

172 """ 

173 assert pow(2, 1) == 2 # on vérifie que 2^1 == 0 

174 assert pow(2, 0) == 1 

175 assert pow(2, -1) == 0.5 

176 assert pow(2, -1) == 0.5 

177 assert pow(0, 0) == 1 

178 assert isinstance(pow(-2, 3.4), complex) 

179 return True 

180 

181 

182def same_variable(a, b): 

183 """ 

184 Cette fonction dit si les deux objets sont en fait le même objet (True) 

185 ou non (False) s'ils sont différents (même s'ils contiennent la même information). 

186 

187 @param a n'importe quel objet 

188 @param b n'importe quel objet 

189 @return ``True`` ou ``False`` 

190 

191 .. faqref:: 

192 :tag: python 

193 :title: Qu'est-ce qu'un type immuable ou immutable ? 

194 :lid: faq-py-immutable 

195 

196 Une variable de type *immuable* ne peut être modifiée. Cela concerne principalement : 

197 

198 - ``int``, ``float``, ``str``, ``tuple`` 

199 

200 Si une variable est de type *immuable*, lorsqu'on effectue une opération, 

201 on créé implicitement une copie de l'objet. 

202 

203 Les dictionnaires et les listes sont *modifiables* (ou *mutable*). Pour une variable 

204 de ce type, lorsqu'on écrit ``a = b``, ``a`` et ``b`` désigne le même objet même 

205 si ce sont deux noms différentes. C'est le même emplacement mémoire 

206 accessible paur deux moyens (deux identifiants). 

207 

208 Par exemple :: 

209 

210 a = (2,3) 

211 b = a 

212 a += (4,5) 

213 print( a == b ) # --> False 

214 print(a,b) # --> (2, 3, 4, 5) (2, 3) 

215 

216 a = [2,3] 

217 b = a 

218 a += [4,5] 

219 print( a == b ) # --> True 

220 print(a,b) # --> [2, 3, 4, 5] [2, 3, 4, 5] 

221 

222 Dans le premier cas, le type (``tuple``) est _immutable_, l'opérateur ``+=`` cache implicitement une copie. 

223 Dans le second cas, le type (``list``) est _mutable_, l'opérateur ``+=`` évite la copie 

224 car la variable peut être modifiée. Même si ``b=a`` est exécutée avant l'instruction suivante, 

225 elle n'a **pas** pour effet de conserver l'état de ``a`` avant l'ajout d'élément. 

226 Un autre exemple :: 

227 

228 a = [1, 2] 

229 b = a 

230 a [0] = -1 

231 print(a) # --> [-1, 2] 

232 print(b) # --> [-1, 2] 

233 

234 Pour copier une liste, il faut expliciter la demander :: 

235 

236 a = [1, 2] 

237 b = list(a) 

238 a [0] = -1 

239 print(a) # --> [-1, 2] 

240 print(b) # --> [1, 2] 

241 

242 La page `Immutable Sequence Types <https://docs.python.org/3/library/stdtypes.html?highlight=immutable#immutable-sequence-types>`_ 

243 détaille un peu plus le type qui sont *mutable* et ceux qui sont *immutable*. Parmi les types standards : 

244 

245 * **mutable** 

246 * `bool <https://docs.python.org/3/library/functions.html#bool>`_ 

247 * `int <https://docs.python.org/3/library/functions.html#int>`_, 

248 `float <https://docs.python.org/3/library/functions.html#float>`_, 

249 `complex <https://docs.python.org/3/library/functions.html#complex>`_ 

250 * `str <https://docs.python.org/3/library/functions.html#func-str>`_, 

251 `bytes <https://docs.python.org/3/library/functions.html#bytes>`_ 

252 * `None <https://docs.python.org/3/library/constants.html?highlight=none#None>`_ 

253 * `tuple <https://docs.python.org/3/library/functions.html#func-tuple>`_, 

254 `frozenset <https://docs.python.org/3/library/functions.html#func-frozenset>`_ 

255 * **immutable**, par défaut tous les autres types dont : 

256 * `list <https://docs.python.org/3/library/functions.html#func-list>`_ 

257 * `dict <https://docs.python.org/3/library/functions.html#func-dict>`_ 

258 * `set <https://docs.python.org/3/library/functions.html#func-set>`_ 

259 * `bytearray <https://docs.python.org/3/library/functions.html#bytearray>`_ 

260 

261 Une instance de classe est mutable. Il est possible de la rendre 

262 immutable par quelques astuces : 

263 

264 * `__slots__ <https://docs.python.org/3/reference/datamodel.html?highlight=_slots__#object.__slots__>`_ 

265 * `How to Create Immutable Classes in Python <http://www.blog.pythonlibrary.org/2014/01/17/ 

266 how-to-create-immutable-classes-in-python/>`_ 

267 * `Ways to make a class immutable in Python <http://stackoverflow.com/questions/4996815/ 

268 ways-to-make-a-class-immutable-in-python>`_ 

269 * `freeze <https://freeze.readthedocs.org/en/latest/>`_ 

270 

271 Enfin, pour les objects qui s'imbriquent les uns dans les autres, une liste de listes, une classe 

272 qui incluent des dictionnaires et des listes, on distingue une copie simple d'une copie intégrale (**deepcopy**). 

273 Dans le cas d'une liste de listes, la copie simple recopie uniquement la première liste :: 

274 

275 import copy 

276 l1 = [ [0,1], [2,3] ] 

277 l2 = copy.copy(l1) 

278 l1 [0][0] = '##' 

279 print(l1,l2) # --> [['##', 1], [2, 3]] [['##', 1], [2, 3]] 

280 

281 l1 [0] = [10,10] 

282 print(l1,l2) # --> [[10, 10], [2, 3]] [['##', 1], [2, 3]] 

283 

284 La copie intégrale recopie également les objets inclus :: 

285 

286 import copy 

287 l1 = [ [0,1], [2,3] ] 

288 l2 = copy.deepcopy(l1) 

289 l1 [0][0] = '##' 

290 print(l1,l2) # --> [['##', 1], [2, 3]] [[0, 1], [2, 3]] 

291 

292 Les deux fonctions s'appliquent à tout object Python : `module copy <https://docs.python.org/3/library/copy.html>`_. 

293 """ 

294 return id(a) == id(b) 

295 

296 

297def stringio(text): 

298 """ 

299 returns a StringIO object on a text 

300 

301 @param text any text 

302 @return StringIO object 

303 

304 .. faqref:: 

305 :tag: python 

306 :title: A quoi sert un ``StringIO`` ? 

307 

308 La plupart du temps, lorsqu'on récupère des données, elles sont sur le disque dur 

309 de votre ordinateur dans un fichier texte. Lorsqu'on souhaite automatiser un processur 

310 qu'on répète souvent avec ce fichier, on écrit une fonction qui prend le nom du fichier en entrée. 

311 

312 :: 

313 

314 def processus_quotidien(nom_fichier) : 

315 # on compte les lignes 

316 nb = 0 

317 with open(nom_fichier,"r") as f : 

318 for line in f : 

319 nb += 1 

320 return nb 

321 

322 Et puis un jour, les données ne sont plus dans un fichier mais sur Internet. 

323 Le plus simple dans ce cas est de recopier ces données sur disque dur et d'appeler la même fonction. 

324 Simple. Un autre les données qu'on doit télécharger font plusieurs gigaoctets. Tout télécharger prend 

325 du temps pour finir pour s'apercevoir qu'elles sont corrompues. On a perdu plusieurs heures pour rien. 

326 On aurait bien voulu que la fonction ``processus_quotidien`` commence à traiter les données 

327 dès le début du téléchargement. 

328 

329 Pour cela, on a inventé la notion de **stream** ou **flux** qui sert d'interface entre la fonction 

330 qui traite les données et la source des données. Le flux lire les données depuis n'importe quel source 

331 (fichier, internet, mémoire), la fonction qui les traite n'a pas besoin d'en connaître la provenance. 

332 

333 `StringIO <https://docs.python.org/3/library/io.html#io.StringIO>`_ est un flux qui considère 

334 la mémoire comme source de données. 

335 

336 :: 

337 

338 def processus_quotidien(data_stream): 

339 # on compte toujours les lignes 

340 nb = 0 

341 for line in data_stream : 

342 nb += 1 

343 return nb 

344 

345 La fonction ``processus_quotidien`` fonctionne pour des données en mémoire 

346 et sur un fichier. 

347 

348 :: 

349 

350 fichier = __file__ 

351 f = open(fichier,"r") 

352 nb = processus_quotidien(f) 

353 print(nb) 

354 

355 text = "ligne1\nligne2" 

356 st = io.StringIO(text) 

357 nb = processus_quotidien(st) 

358 print(nb) 

359 """ 

360 return io.StringIO(text) 

361 

362 

363def property_example(): 

364 """ 

365 

366 .. faqref:: 

367 :tag: python 

368 :title: property 

369 

370 Une `property <https://docs.python.org/3/library/functions.html#property>`_ est 

371 une écriture qui sert à transformer l'appel d'une méthode de classe 

372 en un attribut. 

373 

374 :: 

375 

376 class ClasseAvecProperty: 

377 

378 def __init__(self,x,y): 

379 self._x, self._y = x,y 

380 

381 @property 

382 def x(self): 

383 return self._x 

384 

385 @property 

386 def y(self): 

387 return self._y 

388 

389 @property 

390 def norm2(self): 

391 return self._y**2 + self._x**2 

392 

393 c = ClasseAvecProperty(1,2) 

394 print(c.x) 

395 print(c.y) 

396 

397 ``x`` est définit comme une méthode mais elle retourne simplement l'attribut 

398 ``_x``. De cette façon, il est impossible de changer ``x`` en écrivant:: 

399 

400 c.x = 5 

401 

402 Cela déclenche l'erreur:: 

403 

404 Traceback (most recent call last): 

405 File "faq_python.py", line 455, in <module> 

406 c.x = 5 

407 AttributeError: can't set attribute 

408 

409 On fait cela parce que l'écriture est plus courte et que cela 

410 évite certaines erreurs. 

411 """ 

412 pass 

413 

414 

415def enumerate_regex_search(exp, text): 

416 """ 

417 Cette fonction itère sur les différentes occurences d'une expression régulière. 

418 

419 @param exp expression régulière 

420 @param text text à parser 

421 @return itérateur 

422 

423 .. faqref:: 

424 :tag: python 

425 :title: Comment itérer sur les résultats d'une expression régulière ? 

426 

427 On utilise la méthode `finditer <https://docs.python.org/3/library/re.html#re.regex.finditer>`_. 

428 

429 :: 

430 

431 found = exp.search(text) 

432 for m in exp.finditer(text): 

433 # ... 

434 

435 Voir également `Petites subtilités avec les expressions régulières en Python 

436 <http://www.xavierdupre.fr/blog/2014-12-02_nojs.html>`_. 

437 """ 

438 # found = exp.search(text) 

439 if isinstance(exp, str): 

440 exp = re.compile(exp) 

441 for m in exp.finditer(text): 

442 yield m 

443 

444 

445def download_from_url(url, filename): 

446 """ 

447 

448 downloads a file given a URL and stores it as binary file 

449 

450 @param url url 

451 @param filename local filename 

452 @return filename 

453 

454 .. index:: dropbox 

455 

456 .. faqref:: 

457 :tag: python 

458 :title: Télécharger un fichier depuis un notebook? 

459 

460 L'exemple suivant illustre comment télécharger puis enregister ce fichier 

461 sur le disque local. Il ne faut pas que ce fichier dépasse la taille de la mémoire. 

462 L'url donné en exemple est celui utilisé sur DropBox. 

463 

464 :: 

465 

466 url = "https://www.dropbox.com/[something]/[filename]?dl=1" # dl=1 is important 

467 import urllib.request 

468 with urllib.request.urlopen(url) as u: 

469 data = u.read() 

470 

471 with open([filename], "wb") as f : 

472 f.write(data) 

473 

474 L'exemple est tiré de `Download a file from Dropbox with Python <http://www.xavierdupre.fr/blog/2015-01-20_nojs.html>`_. 

475 """ 

476 with urllib.request.urlopen(url) as u: 

477 data = u.read() 

478 

479 with open(filename, "wb") as f: 

480 f.write(data) 

481 return filename 

482 

483 

484def sortable_class(cl): 

485 """ 

486 .. faqref:: 

487 :tag: python 

488 :title: Classe sortable 

489 

490 Il faut prononcer *sortable* à l'anglaise. Comment rendre une classe 

491 *sortable* ? Pour faire simple, on veut écrire :: 

492 

493 l = [ o1, o2 ] 

494 l.sort() 

495 

496 Où ``o1`` et ``o2`` sont des objets d'une classe 

497 que vous avez définie :: 

498 

499 class MaClasse: 

500 

501 ... 

502 

503 Pour que cela fonctionne, il suffit juste 

504 de surcharger l'opérateur ``<`` ou plus exactement 

505 ``__lt__``. Par exemple :: 

506 

507 class MaClasse: 

508 

509 def __lt__(self, autre_instance): 

510 if self.jenesaispas < autre.jenesaispas: 

511 return True 

512 elif self.jenesaispas > autre.jenesaispas: 

513 return False: 

514 else: 

515 if self.jenesaispas2 < autre.jenesaispas2: 

516 return True 

517 else: 

518 return False 

519 """ 

520 pass 

521 

522 

523def list_of_installed_packages(): 

524 """ 

525 calls ``pip list`` to retrieve the list of packages 

526 

527 .. faqref:: 

528 :tag: python 

529 :title: Obtenir des informations sur les packages installés 

530 

531 Le module :epkg:`pip` retourne des informations 

532 sur n'importe quel module installé, sa version, sa license :: 

533 

534 pip show pandas 

535 

536 On peut également l'obtenir depuis l'interpréteur python :: 

537 

538 import pip 

539 pip.main(["show", "pandas"]) 

540 

541 Exemple :: 

542 

543 Name: pandas 

544 Version: 0.16.0 

545 Summary: Powerful data structures for data analysis, time series,and statistics 

546 Home-page: http://pandas.pydata.org 

547 Author: The PyData Development Team 

548 Author-email: pydata@googlegroups.com 

549 License: BSD 

550 Location: c:\\python35_x64\\lib\\site-packages 

551 Requires: python-dateutil, pytz, numpy 

552 

553 On utilise également ``pip freeze`` pour répliquer l'environnement 

554 dans lequel on a développé un programme. `pip freeze <https://pip.pypa.io/en/latest/reference/pip_freeze.html>`_ 

555 produit la liste des modules avec la version utilisée :: 

556 

557 docutils==0.11 

558 Jinja2==2.7.2 

559 MarkupSafe==0.19 

560 Pygments==1.6 

561 Sphinx==1.2.2 

562 

563 Ce qu'on utilise pour répliquer l'environnement de la manière suivante :: 

564 

565 pip freeze > requirements.txt 

566 pip install -r requirements.txt 

567 

568 Cette façon de faire fonctionne très bien sous Linux mais n'est pas encore 

569 opérationnelle sous Windows à moins d'installer le compilateur C++ utilisée pour compiler 

570 Python. 

571 """ 

572 from pyquickhelper.pycode.pip_helper import get_packages_list 

573 return get_packages_list() 

574 

575 

576def information_about_package(name): 

577 """ 

578 Calls ``pip show`` to retrieve information about packages. 

579 

580 .. faqref:: 

581 :tag: python 

582 :title: Récupérer la liste des modules installés 

583 

584 Le module :epkg:`pip` permet d'installer 

585 de nouveaux modules mais aussi d'obtenir la liste des packages installés :: 

586 

587 pip list 

588 

589 On peut également l'obtenir depuis l'interpréteur python :: 

590 

591 import pip 

592 pip.main(["list"]) 

593 

594 .. faqref:: 

595 :tag: python 

596 :title: Pourquoi l'installation de pandas (ou numpy) ne marche pas sous Windows avec pip ? 

597 

598 Python est un langage très lent et c'est pourquoi la plupart des modules de calculs numériques 

599 incluent des parties implémentées en langage C++. 

600 :epkg:`numpy`, :epkg:`pandas`, :epkg:`matplotlib`, 

601 :epkg:`scipy`, :epkg:`scikit-learn`, 

602 ... 

603 

604 Sous Linux, le compilateur est intégré au système et l'installation de ces modules via 

605 l'instruction ``pip install <module>`` met implicitement le compilateur à contribution. 

606 Sous Windows, il n'existe pas de compilateur C++ par défaut à moins de l'installer. 

607 Il faut faire attention alors d'utiliser exactement le même que celui utilisé 

608 pour compiler Python (voir 

609 `Compiling Python on Windows <https://docs.python.org/3/using/windows.html#compiling-python-on-windows>`_). 

610 

611 C'est pour cela qu'on préfère utiliser des distributions comme 

612 `Anaconda <http://continuum.io/downloads#py34>`_ 

613 qui propose par défaut 

614 une version de Python accompagnée des modules les plus utilisés. Elle propose également une façon 

615 simple d'installer des modules précompilés avec l'instruction :: 

616 

617 conda install <module_compile> 

618 """ 

619 from pyquickhelper.pycode.pip_helper import get_package_info 

620 return get_package_info(name) 

621 

622 

623def get_month_name(date): 

624 """ 

625 returns the month name for a give date 

626 

627 @param date datatime 

628 @return month name 

629 

630 .. faqref:: 

631 :tag: python 

632 :title: Récupérer le nom du mois à partir d'une date 

633 

634 :: 

635 

636 import datetime 

637 dt = datetime.datetime(2016, 1, 1) 

638 print(dt.strftime("%B")) 

639 """ 

640 return date.strftime("%B") 

641 

642 

643def get_day_name(date): 

644 """ 

645 returns the day name for a give date 

646 

647 @param date datatime 

648 @return month name 

649 

650 .. faqref:: 

651 :tag: python 

652 :title: Récupérer le nom du jour à partir d'une date 

653 

654 :: 

655 

656 import datetime 

657 dt = datetime.datetime(2016, 1, 1) 

658 print(dt.strftime("%A")) 

659 """ 

660 return date.strftime("%A")