Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Quelques questions d'ordre général autour du langage Python.
6"""
8import os
9import io
10import re
13def entier_grande_taille():
14 """
16 .. faqref::
17 :tag: python
18 :title: Quel est l'entier le plus grand ?
20 La version 3 du langage Python a supprimé la constante ``sys.maxint``
21 qui définissait l'entier le plus grand (voir
22 `What's New In Python 3.0 <https://docs.python.org/3.1/whatsnew/3.0.html#integers>`_).
23 De ce fait la fonction `getrandbit <https://docs.python.org/3/library/random.html#random.getrandbits>`_
24 retourne un entier aussi grand que l'on veut.
26 ::
28 import random,sys
29 x = random.getrandbits(2048)
30 print(type(x),x)
32 Qui affiche ::
34 <class 'int'> 2882159224557107513165483098383814837021447484558010147211921304219017212673656549681269862792029...
36 Les calculs en nombre réels se font toujours avec huit octets de précision.
37 Au delà, il faut utiliser la librairie `gmpy2 <http://gmpy2.readthedocs.org/en/latest/>`_.
38 Il est également recommandé d'utiliser cette librairie pour les grands nombres entiers
39 (entre 20 et 40 chiffres). La librairie est plus rapide que l'implémentation
40 du langage Python (voir `Overview of gmpy2 <https://gmpy2.readthedocs.org/en/latest/overview.html>`_).
42 .. faqref::
43 :tag: python
44 :title: Tabulations ou espace ?
46 Il est préférable de ne pas utiliser les tabulations et de les remplacer par des espaces.
47 Lorsqu'on passe d'un Editeur à un autre, les espaces ne bougent pas. Les tabulations sont plus ou moins grandes visuellement.
48 L'essentiel est de ne pas mélanger.
49 Dans `SciTE <http://www.scintilla.org/SciTE.html>`_, il faut aller dans le menu Options / Change Indentation Settings...
50 Tous les éditeurs ont une option similaire.
51 """
52 pass
55def difference_div():
56 """
57 .. faqref::
58 :tag: python
59 :title: Quelle est la différence entre / et // - division ?
61 Le résultat de la division avec l'opérateur ``/`` est toujours réel :
62 la division de deux entiers ``1/2`` donne ``0.5``.
63 Le résultat de la division avec l'opérateur ``//`` est toujours entier.
64 Il correspond au quotient de la division.
66 .. runpython::
67 :showcode:
69 div1 = 1/2
70 div2 = 4/2
71 div3 = 1//2
72 div4 = 1.0//2.0
73 print(div1, div2, div3, div4) # affiche (0.5, 2.0, 0, 0)
75 Le reste d'une division entière est obtenue avec l'opérateur ``%``.
77 .. runpython::
78 :showcode:
80 print( 5 % 2 ) # affiche 1
82 C'est uniquement vrai pour les version Python 3.x.
83 Pour les versions 2.x, les opérateurs ``/`` et ``//`` avaient des comportements différents
84 (voir `What’s New In Python 3.0 <https://docs.python.org/3/whatsnew/3.0.html#integers>`_).
85 """
86 div1 = 1 / 2
87 div2 = 4 / 2
88 div3 = 1 // 2
89 div4 = 1.0 // 2.0
90 return div1, div2, div3, div4
93def python_path():
94 """
95 .. faqref::
96 :tag: module
97 :title: Comment éviter sys.path.append... quand on développe un module ?
99 Lorsqu'on développe un module,
100 on ne veut pas l'installer. On ne veut pas qu'il soit présent dans le répertoire ``site-packages`` de la distribution
101 de Python car cela introduit deux versions : celle qu'on développe et celle qu'on a installer.
102 Avant, je faisais cela pour créer un petit programme utilisant mon propre module
103 (et on en trouve quelque trace dans mon code) :
105 ::
107 import sys
108 sys.path.append("c:/moncode/monmodule/src")
109 import monmodule
111 Quand je récupère un programme utilisant ce module, il me faudrait ajouter
112 ces petites lignes à chaque fois et c'est barbant.
113 Pour éviter cela, il est possible de dire à l'interpréteur Python d'aller chercher
114 ailleurs pour trouver des modules en ajoutant le chemin à la
115 `variable d'environnement <http://fr.wikipedia.org/wiki/Variable_d'environnement>`_
116 `PYTHONPATH <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH>`_.
117 Sous Windows :
119 ::
121 set PYTHON_PATH=%PYTHON_PATH%;c:\\moncode\\monmodule\\src
122 """
123 return os.environ.get("PYTHON_PATH", "")
126def same_variable(a, b):
127 """
128 Cette fonction dit si les deux objets sont en fait le même objet (True)
129 ou non (False) s'ils sont différents (même s'ils contiennent la même information).
131 @param a n'importe quel objet
132 @param b n'importe quel objet
133 @return ``True`` ou ``False``
135 .. faqref::
136 :tag: python
137 :title: Qu'est-ce qu'un type immuable ou immutable ?
138 :lid: faq-py-immutable
140 Une variable de type *immuable* ne peut être modifiée. Cela concerne principalement :
142 - ``int``, ``float``, ``str``, ``tuple``
144 Si une variable est de type *immuable*, lorsqu'on effectue une opération,
145 on créé implicitement une copie de l'objet.
147 Les dictionnaires et les listes sont *modifiables* (ou *mutable*). Pour une variable
148 de ce type, lorsqu'on écrit ``a = b``, ``a`` et ``b`` désigne le même objet même
149 si ce sont deux noms différentes. C'est le même emplacement mémoire
150 accessible paur deux moyens (deux identifiants).
152 Par exemple ::
154 a = (2,3)
155 b = a
156 a += (4,5)
157 print( a == b ) # --> False
158 print(a,b) # --> (2, 3, 4, 5) (2, 3)
160 a = [2,3]
161 b = a
162 a += [4,5]
163 print( a == b ) # --> True
164 print(a,b) # --> [2, 3, 4, 5] [2, 3, 4, 5]
166 Dans le premier cas, le type (``tuple``) est _immutable_, l'opérateur ``+=`` cache implicitement une copie.
167 Dans le second cas, le type (``list``) est _mutable_, l'opérateur ``+=`` évite la copie
168 car la variable peut être modifiée. Même si ``b=a`` est exécutée avant l'instruction suivante,
169 elle n'a **pas** pour effet de conserver l'état de ``a`` avant l'ajout d'élément.
170 Un autre exemple ::
172 a = [1, 2]
173 b = a
174 a [0] = -1
175 print(a) # --> [-1, 2]
176 print(b) # --> [-1, 2]
178 Pour copier une liste, il faut expliciter la demander ::
180 a = [1, 2]
181 b = list(a)
182 a [0] = -1
183 print(a) # --> [-1, 2]
184 print(b) # --> [1, 2]
186 La page `Immutable Sequence Types <https://docs.python.org/3/library/stdtypes.html?highlight=immutable#immutable-sequence-types>`_
187 détaille un peu plus le type qui sont *mutable* et ceux qui sont *immutable*. Parmi les types standards :
189 * **mutable**
190 * `bool <https://docs.python.org/3/library/functions.html#bool>`_
191 * `int <https://docs.python.org/3/library/functions.html#int>`_,
192 `float <https://docs.python.org/3/library/functions.html#float>`_,
193 `complex <https://docs.python.org/3/library/functions.html#complex>`_
194 * `str <https://docs.python.org/3/library/functions.html#func-str>`_,
195 `bytes <https://docs.python.org/3/library/functions.html#bytes>`_
196 * `None <https://docs.python.org/3/library/constants.html?highlight=none#None>`_
197 * `tuple <https://docs.python.org/3/library/functions.html#func-tuple>`_,
198 `frozenset <https://docs.python.org/3/library/functions.html#func-frozenset>`_
199 * **immutable**, par défaut tous les autres types dont :
200 * `list <https://docs.python.org/3/library/functions.html#func-list>`_
201 * `dict <https://docs.python.org/3/library/functions.html#func-dict>`_
202 * `set <https://docs.python.org/3/library/functions.html#func-set>`_
203 * `bytearray <https://docs.python.org/3/library/functions.html#bytearray>`_
205 Une instance de classe est mutable. Il est possible de la rendre
206 immutable par quelques astuces :
208 * `__slots__ <https://docs.python.org/3/reference/datamodel.html?highlight=_slots__#object.__slots__>`_
209 * `How to Create Immutable Classes in Python
210 <http://www.blog.pythonlibrary.org/2014/01/17/how-to-create-immutable-classes-in-python/>`_
211 * `Ways to make a class immutable in Python <http://stackoverflow.com/questions/4996815/ways-to-make-a-class-immutable-in-python>`_
212 * `freeze <https://freeze.readthedocs.org/en/latest/>`_
214 Enfin, pour les objects qui s'imbriquent les uns dans les autres, une liste de listes, une classe
215 qui incluent des dictionnaires et des listes, on distingue une copie simple d'une copie intégrale (**deepcopy**).
216 Dans le cas d'une liste de listes, la copie simple recopie uniquement la première liste ::
218 import copy
219 l1 = [ [0,1], [2,3] ]
220 l2 = copy.copy(l1)
221 l1 [0][0] = '##'
222 print(l1,l2) # --> [['##', 1], [2, 3]] [['##', 1], [2, 3]]
224 l1 [0] = [10,10]
225 print(l1,l2) # --> [[10, 10], [2, 3]] [['##', 1], [2, 3]]
227 La copie intégrale recopie également les objets inclus ::
229 import copy
230 l1 = [ [0,1], [2,3] ]
231 l2 = copy.deepcopy(l1)
232 l1 [0][0] = '##'
233 print(l1,l2) # --> [['##', 1], [2, 3]] [[0, 1], [2, 3]]
235 Les deux fonctions s'appliquent à tout object Python : `module copy <https://docs.python.org/3/library/copy.html>`_.
236 """
237 return id(a) == id(b)
240def stringio(text):
241 """
242 returns a StringIO object on a text
244 @param text any text
245 @return StringIO object
247 .. faqref::
248 :tag: python
249 :title: A quoi sert un ``StringIO`` ?
251 La plupart du temps, lorsqu'on récupère des données, elles sont sur le disque dur
252 de votre ordinateur dans un fichier texte. Lorsqu'on souhaite automatiser un processur
253 qu'on répète souvent avec ce fichier, on écrit une fonction qui prend le nom du fichier en entrée.
255 ::
257 def processus_quotidien(nom_fichier) :
258 # on compte les lignes
259 nb = 0
260 with open(nom_fichier,"r") as f :
261 for line in f :
262 nb += 1
263 return nb
265 Et puis un jour, les données ne sont plus dans un fichier mais sur Internet.
266 Le plus simple dans ce cas est de recopier ces données sur disque dur et d'appeler la même fonction.
267 Simple. Un autre les données qu'on doit télécharger font plusieurs gigaoctets. Tout télécharger prend
268 du temps pour finir pour s'apercevoir qu'elles sont corrompues. On a perdu plusieurs heures pour rien.
269 On aurait bien voulu que la fonction ``processus_quotidien`` commence à traiter les données
270 dès le début du téléchargement.
272 Pour cela, on a inventé la notion de **stream** ou **flux** qui sert d'interface entre la fonction
273 qui traite les données et la source des données. Le flux lire les données depuis n'importe quel source
274 (fichier, internet, mémoire), la fonction qui les traite n'a pas besoin d'en connaître la provenance.
276 `StringIO <https://docs.python.org/3/library/io.html#io.StringIO>`_ est un flux qui considère
277 la mémoire comme source de données.
279 ::
281 def processus_quotidien(data_stream):
282 # on compte toujours les lignes
283 nb = 0
284 for line in data_stream :
285 nb += 1
286 return nb
288 La fonction ``processus_quotidien`` fonctionne pour des données en mémoire
289 et sur un fichier.
291 ::
293 fichier = __file__
294 f = open(fichier,"r")
295 nb = processus_quotidien(f)
296 print(nb)
298 text = "ligne1\nligne2"
299 st = io.StringIO(text)
300 nb = processus_quotidien(st)
301 print(nb)
302 """
303 return io.StringIO(text)
306def property_example():
307 """
309 .. faqref::
310 :tag: class
311 :title: property
313 Une `property <https://docs.python.org/3/library/functions.html#property>`_ est
314 une écriture qui sert à transformer l'appel d'une méthode de classe
315 en un attribut.
317 ::
319 class ClasseAvecProperty:
321 def __init__(self,x,y):
322 self._x, self._y = x,y
324 @property
325 def x(self):
326 return self._x
328 @property
329 def y(self):
330 return self._y
332 @property
333 def norm2(self):
334 return self._y**2 + self._x**2
336 c = ClasseAvecProperty(1,2)
337 print(c.x)
338 print(c.y)
340 ``x`` est définit comme une méthode mais elle retourne simplement l'attribut
341 ``_x``. De cette façon, il est impossible de changer ``x`` en écrivant::
343 c.x = 5
345 Cela déclenche l'erreur::
347 Traceback (most recent call last):
348 File "faq_python.py", line 455, in <module>
349 c.x = 5
350 AttributeError: can't set attribute
352 On fait cela parce que l'écriture est plus courte et que cela
353 évite certaines erreurs.
354 """
355 pass
358def enumerate_regex_search(exp, text):
359 """
360 Cette fonction itère sur les différentes occurences d'une expression régulière.
362 @param exp expression régulière
363 @param text text à parser
364 @return itérateur
366 .. faqref::
367 :tag: regex
368 :title: Comment itérer sur les résultats d'une expression régulière ?
370 On utilise la méthode `finditer <https://docs.python.org/3/library/re.html#re.regex.finditer>`_.
372 ::
374 found = exp.search(text)
375 for m in exp.finditer(text):
376 # ...
378 Voir également `Petites subtilités avec les expressions régulières en Python
379 <http://www.xavierdupre.fr/blog/2014-12-02_nojs.html>`_.
380 """
381 # found = exp.search(text)
382 if isinstance(exp, str):
383 exp = re.compile(exp)
384 for m in exp.finditer(text):
385 yield m
388def sortable_class(cl):
389 """
390 .. faqref::
391 :tag: class
392 :title: Classe sortable
394 Il faut prononcer *sortable* à l'anglaise. Comment rendre une classe
395 *sortable* ? Pour faire simple, on veut écrire ::
397 l = [ o1, o2 ]
398 l.sort()
400 Où ``o1`` et ``o2`` sont des objets d'une classe
401 que vous avez définie ::
403 class MaClasse:
405 ...
407 Pour que cela fonctionne, il suffit juste
408 de surcharger l'opérateur ``<`` ou plus exactement
409 ``__lt__``. Par exemple ::
411 class MaClasse:
413 def __lt__(self, autre_instance):
414 if self.jenesaispas < autre.jenesaispas:
415 return True
416 elif self.jenesaispas > autre.jenesaispas:
417 return False:
418 else:
419 if self.jenesaispas2 < autre.jenesaispas2:
420 return True
421 else:
422 return False
423 """
424 pass
427def list_of_installed_packages():
428 """
429 calls ``pip list`` to retrieve the list of packages
431 .. faqref::
432 :tag: module
433 :title: Obtenir des informations sur les packages installés
435 Le module `pip <https://pip.pypa.io/en/stable/>`_ retourne des informations
436 sur n'importe quel module installé, sa version, sa license ::
438 pip show pandas
440 On peut également l'obtenir depuis l'interpréteur python ::
442 import pip
443 pip.main(["show", "pandas"])
445 Exemple ::
447 Name: pandas
448 Version: 0.16.0
449 Summary: Powerful data structures for data analysis, time series,and statistics
450 Home-page: http://pandas.pydata.org
451 Author: The PyData Development Team
452 Author-email: pydata@googlegroups.com
453 License: BSD
454 Location: c:\\python35_x64\\lib\\site-packages
455 Requires: python-dateutil, pytz, numpy
457 On utilise également ``pip freeze`` pour répliquer l'environnement
458 dans lequel on a développé un programme. `pip freeze <https://pip.pypa.io/en/latest/reference/pip_freeze.html>`_
459 produit la liste des modules avec la version utilisée ::
461 docutils==0.11
462 Jinja2==2.7.2
463 MarkupSafe==0.19
464 Pygments==1.6
465 Sphinx==1.2.2
467 Ce qu'on utilise pour répliquer l'environnement de la manière suivante ::
469 pip freeze > requirements.txt
470 pip install -r requirements.txt
472 Cette façon de faire fonctionne très bien sous Linux mais n'est pas encore
473 opérationnelle sous Windows à moins d'installer le compilateur C++ utilisée pour compiler
474 Python.
475 """
476 from pyquickhelper.pycode.pip_helper import get_packages_list # pylint: disable=C0415
477 return get_packages_list()
480def information_about_package(name):
481 """
482 calls ``pip show`` to retrieve information about packages
484 .. faqref::
485 :tag: module
486 :title: Récupérer la liste des modules installés
488 Le module `pip <https://pip.pypa.io/en/stable/>`_ permet d'installer
489 de nouveaux modules mais aussi d'obtenir la liste des packages installés ::
491 pip list
493 On peut également l'obtenir depuis l'interpréteur python ::
495 import pip
496 pip.main(["list"])
498 .. faqref::
499 :tag: python
500 :title: Pourquoi l'installation de pandas (ou numpy) ne marche pas sous Windows avec pip ?
502 Python est un langage très lent et c'est pourquoi la plupart des modules de calculs numériques
503 incluent des parties implémentées en langage C++.
504 `numpy <http://www.numpy.org/>`_,
505 `pandas <http://pandas.pydata.org/>`_,
506 `matplotlib <http://matplotlib.org/>`_,
507 `scipy <http://www.scipy.org/>`_,
508 `scikit-learn <http://scikit-learn.org/stable/>`_,
509 ...
511 Sous Linux, le compilateur est intégré au système et l'installation de ces modules via
512 l'instruction ``pip install <module>`` met implicitement le compilateur à contribution.
513 Sous Windows, il n'existe pas de compilateur C++ par défaut à moins de l'installer.
514 Il faut faire attention alors d'utiliser exactement le même que celui utilisé
515 pour compiler Python (voir
516 `Compiling Python on Windows <https://docs.python.org/3/using/windows.html#compiling-python-on-windows>`_).
518 C'est pour cela qu'on préfère utiliser des distributions comme
519 `Anaconda <http://continuum.io/downloads#py34>`_
520 qui propose par défaut
521 une version de Python accompagnée des modules les plus utilisés. Elle propose également une façon
522 simple d'installer des modules précompilés avec l'instruction ::
524 conda install <module_compile>
526 L'autre option est d'utilser le site
527 `Unofficial Windows Binaries for Python Extension Packages <http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_
528 qui propose des versions compilées sous Windows d'un grand nombre de modules.
529 Il faut télécharger le fichier *.whl* puis l'installer avec l'instruction ``pip install <fichier.whl>``.
530 La différence entre les deux ooptions tient aux environnements virtuels, voir
531 `Python virtual environments <http://astropy.readthedocs.org/en/stable/development/workflow/virtual_pythons.html>`_.
532 """
533 from pyquickhelper.pycode.pip_helper import get_package_info # pylint: disable=C0415
534 return get_package_info(name)
537def get_month_name(date):
538 """
539 returns the month name for a give date
541 @param date datatime
542 @return month name
544 .. faqref::
545 :tag: python
546 :title: Récupérer le nom du mois à partir d'une date
548 .. runpython::
549 :showcode:
551 import datetime
552 dt = datetime.datetime(2016, 1, 1)
553 print(dt.strftime("%B"))
554 """
555 return date.strftime("%B")
558def get_day_name(date):
559 """
560 returns the day name for a give date
562 @param date datatime
563 @return month name
565 .. faqref::
566 :tag: python
567 :title: Récupérer le nom du jour à partir d'une date
569 .. runpython::
570 :showcode:
572 import datetime
573 dt = datetime.datetime(2016, 1, 1)
574 print(dt.strftime("%A"))
575 """
576 return date.strftime("%A")