FAQ#
Langage Python#
Comment itérer sur les résultats d'une expression régulière ?
Comment éviter sys.path.append... quand on développe un module ?
Pourquoi l'installation de pandas (ou numpy) ne marche pas sous Windows avec pip ?
A quoi sert un ``StringIO`` ?
La plupart du temps, lorsqu’on récupère des données, elles sont sur le disque dur de votre ordinateur dans un fichier texte. Lorsqu’on souhaite automatiser un processur qu’on répète souvent avec ce fichier, on écrit une fonction qui prend le nom du fichier en entrée.
def processus_quotidien(nom_fichier) :
# on compte les lignes
nb = 0
with open(nom_fichier,"r") as f :
for line in f :
nb += 1
return nb
Et puis un jour, les données ne sont plus dans un fichier
mais sur Internet.
Le plus simple dans ce cas est de recopier ces données sur disque
dur et d’appeler la même fonction.
Simple. Un autre les données qu’on doit télécharger font plusieurs
gigaoctets. Tout télécharger prend
du temps pour finir pour s’apercevoir qu’elles sont corrompues.
On a perdu plusieurs heures pour rien.
On aurait bien voulu que la fonction processus_quotidien
commence à traiter les données
dès le début du téléchargement.
Pour cela, on a inventé la notion de stream ou flux qui sert d’interface entre la fonction qui traite les données et la source des données. Le flux lire les données depuis n’importe quel source (fichier, internet, mémoire), la fonction qui les traite n’a pas besoin d’en connaître la provenance.
StringIO est un flux qui considère la mémoire comme source de données.
def processus_quotidien(data_stream):
# on compte toujours les lignes
nb = 0
for line in data_stream :
nb += 1
return nb
La fonction processus_quotidien
fonctionne pour des données en mémoire
et sur un fichier.
fichier = __file__
f = open(fichier,"r")
nb = processus_quotidien(f)
print(nb)
text = "ligne1\nligne2"
st = io.StringIO(text)
nb = processus_quotidien(st)
print(nb)
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.stringio, line 6)
Classe sortable
Il faut prononcer sortable à l’anglaise. Comment rendre une classe sortable ? Pour faire simple, on veut écrire
l = [ o1, o2 ]
l.sort()
Où o1
et o2
sont des objets d’une classe
que vous avez définie
class MaClasse:
...
Pour que cela fonctionne, il suffit juste
de surcharger l’opérateur <
ou plus exactement
__lt__
. Par exemple
class MaClasse:
def __lt__(self, autre_instance):
if self.jenesaispas < autre.jenesaispas:
return True
elif self.jenesaispas > autre.jenesaispas:
return False:
else:
if self.jenesaispas2 < autre.jenesaispas2:
return True
else:
return False
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.sortable_class, line 1)
Comment itérer sur les résultats d’une expression régulière ?
On utilise la méthode finditer.
found = exp.search(text)
for m in exp.finditer(text):
# ...
Voir également Petites subtilités avec les expressions régulières en Python.
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.enumerate_regex_search, line 8)
Comment éviter sys.path.append… quand on développe un module ?
Lorsqu’on développe un module,
on ne veut pas l’installer. On ne veut pas qu’il soit présent
dans le répertoire site-packages
de la distribution
de Python car cela introduit deux versions :
celle qu’on développe et celle qu’on a installer.
Avant, je faisais cela pour créer un petit
programme utilisant mon propre module
(et on en trouve quelque trace dans mon code) :
import sys
sys.path.append("c:/moncode/monmodule/src")
import monmodule
Quand je récupère un programme utilisant ce module, il me faudrait ajouter ces petites lignes à chaque fois et c’est barbant. Pour éviter cela, il est possible de dire à l’interpréteur Python d’aller chercher ailleurs pour trouver des modules en ajoutant le chemin à la variable d’environnement PYTHONPATH. Sous Windows :
set PYTHON_PATH=%PYTHON_PATH%;c:\moncode\monmodule\src
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.python_path, line 1)
Obtenir des informations sur les packages installés
Le module pip retourne des informations sur n’importe quel module installé, sa version, sa license
pip show pandas
On peut également l’obtenir depuis l’interpréteur python
import pip
pip.main(["show", "pandas"])
Exemple
Name: pandas
Version: 0.16.0
Summary: Powerful data structures for data analysis,
time series,and statistics
Home-page: http://pandas.pydata.org
Author: The PyData Development Team
Author-email: pydata@googlegroups.com
License: BSD
Location: c:\python35_x64\lib\site-packages
Requires: python-dateutil, pytz, numpy
On utilise également pip freeze
pour répliquer l’environnement
dans lequel on a développé un programme.
pip freeze
produit la liste des modules avec la version utilisée
docutils==0.11
Jinja2==2.7.2
MarkupSafe==0.19
Pygments==1.6
Sphinx==1.2.2
Ce qu’on utilise pour répliquer l’environnement de la manière suivante
pip freeze > requirements.txt
pip install -r requirements.txt
Cette façon de faire fonctionne très bien sous Linux mais n’est pas encore opérationnelle sous Windows à moins d’installer le compilateur C++ utilisée pour compiler Python.
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.list_of_installed_packages, line 3)
Pourquoi l’installation de pandas (ou numpy) ne marche pas sous Windows avec pip ?
Python est un langage très lent et c’est pourquoi la plupart des modules de calculs numériques incluent des parties implémentées en langage C++. numpy, pandas, matplotlib, scipy, scikit-learn, … …
Sous Linux, le compilateur est intégré au système et
l’installation de ces modules via
l’instruction pip install <module>
met implicitement
le compilateur à contribution.
Sous Windows, il n’existe pas de compilateur C++ par
défaut à moins de l’installer.
Il faut faire attention alors d’utiliser exactement le même que celui utilisé
pour compiler Python (voir
Compiling Python on Windows).
C’est pour cela qu’on préfère utiliser des distributions comme Anaconda qui propose par défaut une version de Python accompagnée des modules les plus utilisés. Elle propose également une façon simple d’installer des modules précompilés avec l’instruction
conda install <module_compile>
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.information_about_package, line 18)
Python n’accepte pas les accents
Le langage Python a été conçu en langage anglais. Dès qu’on on ajoute un caractère qui ne fait pas partie de l’alphabet anglais (ponctuation comprise), il déclenche une erreur :
File "faq_cvxopt.py", line 3
SyntaxError: Non-UTF-8 code starting with 'è' in
file faq_cvxopt.py on line 4, but no encoding declared;
see http://python.org/dev/peps/pep-0263/ for details
Pour la résoudre, il faut dire à l’interpréteur que des caractères non anglais peuvent apparaître et écrire sur la première ligne du programme :
# -*- coding: latin-1 -*-
Ou pour tout caractère y compris chinois :
# -*- coding: utf-8 -*-
Si vous utilisez l’éditeur SciTE sous Windows, après avoir ajouté cette ligne avec l’encoding utf-8, il est conseillé de fermer le fichier puis de le réouvrir. SciTE le traitera différemment.
L’encodage ``utf-8`` est la norme sur Internet. C’est pourquoi il est préférable d’utiliser celui-ci pour partager son code via une page Web.
(entrée originale : classiques.py:docstring of teachpyx.examples.classiques.commentaire_accentues, line 3)
Qu’est-ce qu’un type immuable ou immutable ?
Une variable de type immuable ne peut être modifiée. Cela concerne principalement :
int
,float
,str
,tuple
Si une variable est de type immuable, lorsqu’on effectue une opération, on créé implicitement une copie de l’objet.
Les dictionnaires et les listes sont
modifiables (ou mutable). Pour une variable
de ce type, lorsqu’on écrit a = b
, a
et b
désigne le même objet même
si ce sont deux noms différentes.
C’est le même emplacement mémoire
accessible paur deux moyens (deux identifiants).
Par exemple
a = (2,3)
b = a
a += (4,5)
print( a == b ) # --> False
print(a,b) # --> (2, 3, 4, 5) (2, 3)
a = [2,3]
b = a
a += [4,5]
print( a == b ) # --> True
print(a,b) # --> [2, 3, 4, 5] [2, 3, 4, 5]
Dans le premier cas, le type (tuple
) est _immutable_,
l’opérateur +=
cache implicitement une copie.
Dans le second cas, le type (list
) est _mutable_,
l’opérateur +=
évite la copie
car la variable peut être modifiée. Même si b=a
est exécutée avant l’instruction suivante,
elle n’a pas pour effet de conserver l’état de a
avant
l’ajout d’élément.
Un autre exemple
a = [1, 2]
b = a
a [0] = -1
print(a) # --> [-1, 2]
print(b) # --> [-1, 2]
Pour copier une liste, il faut expliciter la demander
a = [1, 2]
b = list(a)
a [0] = -1
print(a) # --> [-1, 2]
print(b) # --> [1, 2]
La page Immutable Sequence Types détaille un peu plus le type qui sont mutable et ceux qui sont immutable. Parmi les types standards :
Une instance de classe est mutable. Il est possible de la rendre immutable par quelques astuces :
surcharger des méthodes __getattr__, __getattribute__, __setattr__.
Enfin, pour les objects qui s’imbriquent les uns dans les autres, une liste de listes, une classe qui incluent des dictionnaires et des listes, on distingue une copie simple d’une copie intégrale (deepcopy). Dans le cas d’une liste de listes, la copie simple recopie uniquement la première liste
import copy
l1 = [ [0,1], [2,3] ]
l2 = copy.copy(l1)
l1 [0][0] = '##'
print(l1, l2) # --> [['##', 1], [2, 3]] [['##', 1], [2, 3]]
l1 [0] = [10,10]
print(l1,l2) # --> [[10, 10], [2, 3]] [['##', 1], [2, 3]]
La copie intégrale recopie également les objets inclus
import copy
l1 = [ [0,1], [2,3] ]
l2 = copy.deepcopy(l1)
l1 [0][0] = '##'
print(l1,l2) # --> [['##', 1], [2, 3]] [[0, 1], [2, 3]]
Les deux fonctions s’appliquent à tout object Python : module copy.
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.same_variable, line 9)
Quel est l’entier le plus grand ?
La version 3 du langage Python a supprimé la constante sys.maxint
qui définissait l’entier le plus grand (voir
What’s New In Python 3.0).
De ce fait la fonction
getrandbit
retourne un entier aussi grand que l’on veut.
import random,sys
x = random.getrandbits(2048)
print(type(x),x)
Qui affiche
<class 'int'> 2882159224557107513165483098383814837021447484558010147211921
304219017212673656549681269862792029...
Les calculs en nombre réels se font toujours avec huit octets de précision. Au delà, il faut utiliser la librairie gmpy2. Il est également recommandé d’utiliser cette librairie pour les grands nombres entiers (entre 20 et 40 chiffres). La librairie est plus rapide que l’implémentation du langage Python (voir Overview of gmpy2).
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.entier_grande_taille, line 1)
Quelle est la différence entre / et // - division ?
Le résultat de la division avec l’opérateur /
est toujours réel :
la division de deux entiers 1/2
donne 0.5
.
Le résultat de la division avec l’opérateur //
est toujours entier.
Il correspond au quotient de la division.
<<<
div1 = 1 / 2
div2 = 4 / 2
div3 = 1 // 2
div4 = 1.0 // 2.0
print(div1, div2, div3, div4) # affiche (0.5, 2.0, 0, 0)
>>>
0.5 2.0 0 0.0
Le reste d’une division entière est obtenue avec l’opérateur %
.
<<<
print(5 % 2) # affiche 1
>>>
1
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.difference_div, line 1)
Quelle est la différence entre return et print ?
La fonction print
sert à afficher un résultat sur la sortie standard.
Elle peut être utilisée à tout moment
mais elle n’a pas d’impact sur le déroulement programme. Le mot-clé return
n’est utilisé que dans une fonction. Lorsque le programme rencontre
une instruction commençant par return
, il quitte la fonction
et transmet le résultat à l’instruction qui a appelé la fonction.
La fonction print
ne modifie pas votre algorithme. La fonction return
spécifie le résultat de votre fonction : elle modifie l’algorithme.
(entrée originale : classiques.py:docstring of teachpyx.examples.classiques.dix_entiers_carre, line 5)
Récupérer la liste des modules installés
Le module pip permet d’installer de nouveaux modules mais aussi d’obtenir la liste des packages installés
pip list
On peut également l’obtenir depuis l’interpréteur python
import pip
pip.main(["list"])
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.information_about_package, line 3)
Récupérer le nom du jour à partir d’une date
<<<
import datetime
dt = datetime.datetime(2016, 1, 1)
print(dt.strftime("%A"))
>>>
Friday
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.get_day_name, line 6)
Récupérer le nom du mois à partir d’une date
<<<
import datetime
dt = datetime.datetime(2016, 1, 1)
print(dt.strftime("%B"))
>>>
January
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.get_month_name, line 6)
Tabulations ou espace ?
Il est préférable de ne pas utiliser les tabulations et de les remplacer par des espaces. Lorsqu’on passe d’un Editeur à un autre, les espaces ne bougent pas. Les tabulations sont plus ou moins grandes visuellement. L’essentiel est de ne pas mélanger. Dans SciTE, il faut aller dans le menu Options / Change Indentation Settings… Tous les éditeurs ont une option similaire.
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.entier_grande_taille, line 35)
propriété
Une property est une écriture qui sert à transformer l’appel d’une méthode de classe en un attribut.
class ClasseAvecProperty:
def __init__(self,x,y):
self._x, self._y = x,y
@property
def x(self):
return self._x
@property
def y(self):
return self._y
@property
def norm2(self):
return self._y**2 + self._x**2
c = ClasseAvecProperty(1,2)
print(c.x)
print(c.y)
x
est définit comme une méthode mais elle
retourne simplement l’attribut
_x
. De cette façon, il est impossible de
changer x
en écrivant:
c.x = 5
Cela déclenche l’erreur:
Traceback (most recent call last):
File "faq_python.py", line 455, in <module>
c.x = 5
AttributeError: can't set attribute
On fait cela parce que l’écriture est plus courte et que cela évite certaines erreurs.
(entrée originale : faq_python.py:docstring of teachpyx.faq.faq_python.property_example, line 1)
numpy#
Quels sont les types que numpy supporte ?
Lire basic types. numpy propose plus de types que Python, les mêmes que le langage C (langage de son implémentation). Les programmeurs cherchent toujours le plus petit type possible pour représenter un nombre. Si une matrice ne possède que des entiers entre 0 et 255, on peut utiliser le type numpy.uint8 qui est codé sur un octet. Cela explique pourquoi beaucoup de libraires de machine learning sont codées des numpy.float32, soit 4 octets plutôt que numpy.float64 ou double. Deux raisons à cela, les numpy.float32 prennent deux fois moins de place en mémoire. Le coût des calculs avec des double est plus coûteux avec les GPU. Lire Explaining FP64 performance on GPUs.
(entrée originale : numpysex.py:docstring of teachpyx.examples.numpysex.numpy_types, line 7)