1. Rappel de l'atelier 2
Voir https://github.com/HackYourPhd/ateliers-open-geek/blob/master/Atelier%232_Notes.md
2. Différence entre language interprété et compilé
3. La gestion de la mémoire en Python
Façon dont fonctionne la mémoire en python, pas comme les autres langages : déjà, Python s'occupe de la mémoire tout seul.
Ensuite, la mémoire est séparée en deux : - Espace de nom - Mémoire Python
Quand on fait a=5
, cela crée le nom a
dans l'espace de nom et on stoque la valeur 5 en mémoire.
Si l'on fait b=a
, cela crée un nouveau nom b
dans l'espace de nom mais la valeur 5 reste la même.
Quand on definit une variable, on assigne un nom et une valeur:
variable = nom associé à une valeur
Pour plus de détail: http://fr.wikibooks.org/wiki/Programmation_Python/Variable
Créer un fichier Python: hello.py Écrire dedans:
print "hello world!"
Pour le lancer faire dans un shell (ligne de commande):
python hello.py
Le format d'un programme Python est généralement:
# -*- coding: utf-8 -*- #
"""
Description du programme: blablabla
Auteur: John Doe
Date: 21/01/2015
"""
print "Hello!"
On peut aussi y définir des fonctions comme par exemple:
def CalcAge(annee_naissance):
return 2015-annee_naissance
Attention: il faut 4 espaces (" ") à la deuxième ligne et pas une tabulation (sauf si on a un bon éditeur de texte).
Remarquez que nous avons ajouté en début de script:
# -*- coding: utf-8 -*-
Cela indique au programme qui interprête le code quel format de caractère on a utilisé et ainsi éviter les soucis avec les encodage de texte.
Voir la pep8 : guide des bonnes pratiques pour bien écrire le code.
https://www.python.org/dev/peps/pep-0008/
L'idéal est d'utiliser un bon éditeur de code. On conseille par exemple d'utiliser Spyder qui permet d'avoir à la fois un interpréteur et une interface d'édition de script.
Configuration de spyder pour exécuter le programme : aller dans l'onglet 'exécution' puis 'configurer l'exécution' et choisir d'ouvrir dans une nouvelle console Python.
Chaque fois, cela ouvrira une nouvelle console propre et exécute le programme dedans. Il faut procéder ainsi quand on veut vérifier qu'un programme marche (sinon, source de problèmes avec la mémoire notamment).
Testons notre fonction:
def CalcAge(annee_naissance):
return 2015-annee_naissance
print "Vous avez", CalcAge(1977), "ans."
Vous avez 38 ans.
print "Vous avez", CalcAge(20055), "ans."
Vous avez -18040 ans.
Le résultat précédent est débile => il faudrait vérifier les entrées. Le test est la plus simple des structures de contrôle
if annee_naissance > 2015:
print "tu bluffes"
else:
return 2015-annee_naissance
On voit émerger une caracteristique de Pyhton : on delimite les blocs d'instruction par l'indentation (les 4 caractères espace).
Cela saute aux yeuw, mais certains détestent ça.
Toute ligne qui se termine par un :
introduit un bloc.
La ligne avec les :
s'appelle une entête de bloc, et en dessous avec l'indentation plus grande, c'est le corps du bloc
Dans le cas du test ci-dessus: si la condition est vraie, on execute le premier bloc, si elle est fausse, on exécute second bloc.
Notez qu'une bonne pratique est de séparer les entrees sorties des traitements. Ici, ce n'est pas tout a fait ça car il y a une sortie "tu bluffes"
def CalcAge(annee_naissance):
if annee_naissance > 2015:
print "tu bluffes"
else:
return 2015-annee_naissance
mon_annee = 2048
age = CalcAge(mon_annee)
print "Vous avez", age, "ans."
tu bluffes Vous avez None ans.
C'est quoi ce None
?
C'est un truc qui dit qu'il n'y a pas de valeur. En Python une fonction renvoit toujours une valeur.
Quand il y a une erreur, il y a une erreur, on va voir comment en déclancher une sans rentrer dans les détails.
CalcAge("Test")
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-5-609e626bb253> in <module>() ----> 1 CalcAge("Test") <ipython-input-3-4eb31cc7b070> in CalcAge(annee_naissance) 1 def CalcAge(annee_naissance): ----> 2 return 2015-annee_naissance 3 print "Vous avez", CalcAge(1977), "ans." TypeError: unsupported operand type(s) for -: 'int' and 'str'
TypeError: c'est une exception indiquant que nous avons entré une variable de type incorrect. La fonction veut un nombre et on lui a donné une chaîne de caractère.
ValueError, c'est une autre exception. C'est cette fois un type d'exception, qui dit que l'erreur est une valeur problematique
Remarquez la pile d'execution au niveau des erreurs: cela montre la chaine d'execution jusqu'à l'errur: ici, c'est une fonction (CalcAge) qui appelle une autre fonction (la soustraction) qui produit l'erreur.
Il est possible de créer c'est propre exceptions:
def CalcAge(annee_naissance):
if annee_naissance > 2015:
msg="Année de naissance invalide : {0}".format(annee_naissance)
raise ValueError(msg)
else:
return 2015-annee_naissance
return None
mon_annee = 2048
#là c'est le traitement !
age = CalcAge(mon_annee)
print "Vous avez", age, "ans."
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-9-0bacac4e734e> in <module>() 9 mon_annee = 2048 10 #là c'est le traitement ! ---> 11 age = CalcAge(mon_annee) 12 13 print "Vous avez", age, "ans." <ipython-input-9-0bacac4e734e> in CalcAge(annee_naissance) 2 if annee_naissance > 2015: 3 msg="Année de naissance invalide : {0}".format(annee_naissance) ----> 4 raise ValueError(msg) 5 else: 6 return 2015-annee_naissance ValueError: Année de naissance invalide : 2048
On peut aussi interagir avec l'utilisateur avec la fonction raw_input
:
mon_annee = raw_input("Votre année de naissance ?")
age = CalcAge(mon_annee)
print "Vous avez", age, "ans."
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) <ipython-input-6-19449f339ef6> in <module>() ----> 1 mon_annee = raw_input("Votre année de naissance ?") 2 age = CalcAge(mon_annee) 3 print "Vous avez", age, "ans." /Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/kernel/zmq/ipkernel.pyc in <lambda>(prompt) 361 # raw_input in the user namespace. 362 if content.get('allow_stdin', False): --> 363 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent) 364 input = lambda prompt='': eval(raw_input(prompt)) 365 else: /Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/kernel/zmq/ipkernel.pyc in _raw_input(self, prompt, ident, parent) 763 except KeyboardInterrupt: 764 # re-raise KeyboardInterrupt, to truncate traceback --> 765 raise KeyboardInterrupt 766 else: 767 break KeyboardInterrupt:
Le problème est que raw_input
renvoie une chaine de caractere, pas un nombre!
En Pyhton 2, quand on fait des comparaisons entre des pommes et des poires, même si cela n'a aucun sens, cela va tout de même decreter qu'un est plus grand que l'autre par principe.
Ici, quand on compare, pas de problème. Mais par contre, on sort une reponse qui est toujours la même, mais pas définie.
Attention, ne jamais utilisez input
car on n'est jamais sùr de ce qu'on récupère, et surtout c'est une potentielle faille de sécurité si quelqu'un rentre du code Python!
Du coup, on utilise: raw_input
et même int(raw_input(""))
pour forcer le renvoie d'un nombre entier. C'est non seulement plus sùr mais si on n'a pas rentré un truc valide, alors on voit une erreur directement.
Si l'on veut générer les tables de multiplication automatiquement, il nous faut faire des boucles:
table = 5
for i in range(1,10):
print "{0} x {1} = {2}".format(table, i, table * i)
5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25 5 x 6 = 30 5 x 7 = 35 5 x 8 = 40 5 x 9 = 45
Mais comme on voudrait séparer les entrées, les dorties et les traitements, on va créer une liste = notre premiére structure de donnée.
table = 5
l=[]
for i in range(1,10):
l.append(table * i)
print l
for i, r in enumerate(l):
print "{0} x {1} = {2}".format(table, i, r)
[5, 10, 15, 20, 25, 30, 35, 40, 45] 5 x 0 = 5 5 x 1 = 10 5 x 2 = 15 5 x 3 = 20 5 x 4 = 25 5 x 5 = 30 5 x 6 = 35 5 x 7 = 40 5 x 8 = 45
Ah... cela s'arrete a 8! Du coup, on proprifie tout ça!
table = 5
l=[]
for i in range(1,11):
l.append(table * i)
print l
for i, r in enumerate(l,1):
print "{0} x {1} = {2}".format(table, i, r)
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50] 5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25 5 x 6 = 30 5 x 7 = 35 5 x 8 = 40 5 x 9 = 45 5 x 10 = 50
Là il y a eu plein de chose ajoutées.
l
c'est la liste des valeurs de ma table.
Là structure de données liste permet de ranger des données les unes derrière les autres, dans l'ordre et d'aller les chercher en disant le quantième élement, attention on part de 0.
La methode append de l'objet liste permet d'ajouter des éléments à notre liste.
print range(1,11) #fabrique la liste des entiers de 1 à 11, 11 exclu
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
description de la première boucle : l prend les valeurs et on fait le calcul
enumerate
= petite astuce filou!
façon plus simple mais moins Python serait de refaire un:
for i in range(1,11):
print "{0} x {1} = {2}".format(table, i, l[i])
Mouais, mais ça marche pas, il faut un i-1
Allez, on va faire une fonction moyenne qui prend en argument liste de nombre et sort!
Info utile : pour liste l
, len(l)
retourne la longueur de la liste
liste=[1,2,3,4,5,6,7]
def m(l): return float(sum(l))/len(l)
print "Moyenne de ",l," : ",m(liste)
Moyenne de [1, 2, 3] : 4.0
liste=[1,2,3,4,5,6,7]
def m(l):
try:
compteur=0
total=0
while True:
compteur+=1
total=total + l[compteur]
except IndexError:
return float(total)/compteur
m(liste)
3.857142857142857
Dans la façn de faire, on commence par créer le squelette:
def moyenne(l):
return 0
liste=[1,2,3]
m=moyenne(liste)
print "Moyenne de ",l," : ",m
Moyenne de [1, 2, 3] : 0
Puis, une fois le squelette posé, on peut ensuite modifier le code pour faire la fonction comme il faut.
Sinon, on peut faire des trucs et ensuite inclure le tout dans une fonction.
def moyenne(l):
total = 0
for i in l:
total = total + i
moy = float(total) / len(l)
return moy
liste=[1,2,3]
m = moyenne(liste)
print "Moyenne de ",l ," : ", m
Moyenne de [1, 2, 3] : 2.0
Notez que dans Spyder on peut voir ce qu'il y a dans l'explorateur de variables.
Ici, pourtant, moy
et total
n'y sont pas... Pourquoi?
Quand on rentre dans une fonction on a un espace local de nom pour la fonction => schéma avec espace de nom et espace local.
À le nom moy
, avec la valeur de retour (return
), n'existe que dans la fonction. Quand la fonction se termine, l'espace de nom local de la fonction est detruit.
Du coup, quand on change la valeur de variable ds la fonction, ça ne se voit pas dans l'explorateur de variables de Spyder.
En Python on ne manipule que des pointeurs qui pointent vers des objets. La valeur de retour de la fonction c'est ce qui va pointer vers le truc en memoire. Le nom qui permet d'acceder a la donnee disparait. Si la donnée reste accessible par ailleurs, alors on peut la retrouver, sinon elle est perdue.
t=5,6
print t, '\n', type(t), '\n', len(t)
(5, 6) <type 'tuple'> 2
t
est un tupple. Cela sert surtout pour renvoyer plusieurs valeurs et c'est très utile pour ne pas écrire des indices partout.
Si on veut que notre fonction nous renvoie deux trucs, par exemple la moyenne et la variance, alors on utilise ces tuples.
def moyenne_et_variance(l):
total = 0
for i in l:
total = total + i
moy = float(total) / len(l)
var = 0
for i in l:
var = var + (i-moy)**2
return moy, var
liste=[1,2,3,4,5]
m, v = moyenne_et_variance(liste)
print "Pour ", liste ," la moyenne est ", m, " et la variance est ", v
Pour [1, 2, 3, 4, 5] la moyenne est 3.0 et la variance est 10.0
On peut aussi faire:
def moyenne_et_variance(l):
total = 0
for i in l:
total = total + i
moy = float(total) / len(l)
var = 0
for i in l:
var = var + (i-moy)**2
r=(moy,var)
return r
liste=[1,2,3,4,5]
m, v = moyenne_et_variance(liste)
print "Pour ", liste ," la moyenne est ", m, " et la variance est ", v
Pour [1, 2, 3, 4, 5] la moyenne est 3.0 et la variance est 10.0
Notez que pour toutes les séquences (liste), on peut les parcourir avec for
et en attraper un element avec des crochets et l'indice correspondant.
On peut modifier les séquence, mais on ne peut pas modifier les tupples. Par exemple, une chaine de caractère est un tuple en Python. Du coup, les chaines sont internalisees (compare les references).
Python donne aussi accès aux ensembles, le type de variable set
.
s={1,2}
print s,'\n', type(s),'\n', s[1]
set([1, 2]) <type 'set'>
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-17-5010758e1a4a> in <module>() 1 s={1,2} ----> 2 print s,'\n', type(s),'\n', s[1] TypeError: 'set' object does not support indexing
On remarque que les ensembles sont non indexés. Par contre il existe les opération mathematiques classique pour les ensemble : union, intersection, appartenance etc.
a={1,2,3}
b={2,3,4}
print "a union b =", a.union(b)
print "a intersection b =", a.intersection(b)
print "1 is in a:", 1 in a
print "1 is in b:", 1 in b
a union b = set([1, 2, 3, 4]) a intersection b = set([2, 3]) 1 is in a: True 1 is in b: False
La prochaine fois nous verrons les dictionaires. On pourra aussi regarder un problème qui vous concerne et voir ce qu'on peut faire en Python.