Python
¶Python
est un langage de programmation en pleine croissance dans le domaine de la Data Science et du Big Data. Il a une syntaxe particulière pour certains aspects, avec des points communs tout de même avec de nombreux autres. Nous allons ici aborder les premiers éléments de syntaxe de ce langage.
python
est un langage scripté, dont l'exécution se fait dans une console. Dans celle-ci, il est donc possible d'exécuter les commandes les unes après les autres. Il est aussi possible (et recommander) d'écrire son script dans un fichier texte (souvent avec l'extension .py
) et de l'exécuter via execfile()
.
Pour accéder à l'aide d'une fonction, il existe la fonction help()
, prenant éventuellement en paramètre une fonction directement, ou une chaîne de caractère. Si la fonction n'a pas de paramètre, elle démarre l'aide interactive.
Le module jupyter
permet de travailler avec des notebook, qui sont des documents contenant à la fois le code, les résultat et du texte. Pour pouvoir l'utiliser, il faut d'abord installer le module dans python, avec la commande pip3
dans un terminale de commande.
{bash}
pip3 install jupyter
Une fois le module installé, il est possible de lancer le serveur jupyter
avec la commande suivante, à faire dans un terminale de commande.
{bash}
jupyter notebook
Il faut appuyer sur Ctrl + C
pour stopper le processus.
Comme tous les autres langages, python
a plusieurs types possibles pour les données. En voici quelques uns. Vous pouvez exécuter les commandes dans une console pour voir le résultat.
1
type(1)
1.234
type(1.234)
"chaîne"
type("chaîne")
(1, 2)
type((1, 2))
[1, 2]
type([1, 2])
{"a": 1, "b": "deux"}
type({"a": 1, "b": "deux"})
Les tuples
, lists
et les dicts
peuvent s'imbriquer les uns dans les autres.
(1, (2, 3), [4, 5], {"a": 1, "b": "deux"})
[1, [2, 3], (4, 5), {"a": 1, "b": "deux"}]
{"a": 1, "b": "deux", "c": (5, 6), "d": [7, 8]}
Il existe des fonctions permettant de passer d'un type à l'autre (quand cela est possible), telles que int()
, float()
, str()
, tuple()
et list()
.
Il existe aussi des valeurs prédéfinies, telles que True
(vrai), False
(faux) et None
(donnée absente).
Il n'y a pas de mot-clé pour la définition d'une variable. Celle-ci est définie/créée lors de sa première affectation. Si elle n'existe pas mais qu'on essaie de l'utiliser, alors un message d'erreur apparaît. Il est aussi possible de la supprimer via la fonction del()
.
a = 1
print(a)
print(b)
del(a)
print(a)
Bien que python
soit rigoureux dans l'évaluation des expressions (il ne fait pas de cast
automatique - i.e. changement de type des données), le type d'une variable est dit dynamique. Le type d'une variable dépend uniquement de la valeur de son affectation. Voici un exemple simple de ce phénomène.
a = 1
print(a)
type(a)
a = "deux"
print(a)
type(a)
La variable a
est passé du type int
au type str
sans qu'on l'explicite. Il faut donc faire attention lors de l'écriture de ses programmes.
Comme vu précédemment, il existe la fonction print()
permettant d'afficher du texte et/ou le contenu des variables dans la console. Celle-ci peut prendre les paramètres sep
, qui permet d'indiquer le ou les caractères séparant les champs (un espace " "
par défaut), et end
, qui permet d'indiquer le caractère de fin de ligne (retour à la ligne "\n"
par défaut).
print("Bonjour")
print("a =", a)
print("a", a, sep = "=")
print("a=", end = "")
print(a)
5 + 2
5 - 2
5 * 2
5 / 2
5 // 2
5 % 2
5 ** 2
De même pour les opérateurs de comparaisons, tous très classiques.
5 > 2
5 >= 2
5 < 2
5 <= 2
5 == 2
5 != 2
Idem aussi pour les opérateurs booléens. Il faut juste noter la fonction not()
pour obtenir la négation booléenne.
True | False
True & True
not(True)
a = 3
if (a > 2):
print("sup")
Une particularité de python
est d'utiliser l'indentation (i.e. le décalage à droite à l'aide d'au moins une tabulation) pour définir les opérations à réaliser dans un bloc. Si on veut faire plusieurs opérations dans le if
, voila comment procéder par exemple.
if (a > 2):
print("dans le IF")
print("sup")
Il existe aussi la possibilité d'ajouter soit un traitement alternatif simple (avec else
), soit un traitement alternatif conditionnel lui aussi (avec elif
).
# Avec un else seulement
a = 1
if (a > 2):
print("sup")
else:
print("inf")
# Avec elif en plus
if (a > 2):
print("sup")
elif (a > 0):
print("mid")
else:
print("inf")
Il n'existe rien dans python
pour le switch/case
tel qu'on peut le voir par ailleurs. Mais on peut passer par un dictionnaire pour des tests d'égalité (cf plus bas pour plus d'informations sur les dictionnaires).
jour = {
0: "lundi",
1: "mardi",
2: "mercredi",
3: "jeudi",
4: "vendredi",
5: "samedi",
6: "dimanche"
}
jour.get(2)
On utilise en premier la boucle for
dans laquelle on peut utiliser la fonction range()
pour avoir les valeurs entre 0
(par défaut) et la valeur passée en paramètre. Vous remarquerez que i
est persistant à la boucle et garde la dernière valeur.
for i in range(5):
print(i)
print("dernière valeur de i :", i)
Cette fonction range()
peut prendre deux paramètres, et dans ce cas, génère la boucle entre les deux par pas de 1.
for i in range(5, 10):
print(i)
Et si l'on souhaite modifier le pas de séquence, on ajoute un troisième paramètre. Celui-ci doit être cohérent par rapport aux deux premières valeurs.
for i in range(10, 5, -1):
print(i)
Il est possible d'utiliser une list
ou un tuple
pour définir les valeurs dans lesquelles naviguer.
for i in [4, 1, 10]:
print(i)
Et en utilisant une chaîne de caractère, on peut naviguer dans celle-ci.
for l in "Bonjour":
print(l)
Mais en utilisant un groupe de chaîne (list
ou tuple
), on travaille sur les chaînes au complet.
for l in ("jour", "soir"):
print("Bon", l, sep = "")
La fonction enumerate()
permet de récupérer à la fois les indices des valeurs et les valeurs.
a = [3, 1, 9, 4]
for i, x in enumerate(a):
print("i =", i, "\tx =", x)
Et la fonction zip()
permet elle de travailler sur plusieurs groupes de valeurs (ici deux listes). Notez que cette fonction limite le résultat à la taille du plus petit regroupement.
b = ["trois", "un", "neuf"]
for i, j in zip(a, b):
print(" i =", i, "\tj =", j)
Enfin, on dispose aussi de la boucle while
qui teste en début de boucle si une condition est toujours vérifiée. Bien évidemment, à la fin de la boucle, i
a la première valeur à rendre la condition fausse. Ici, i += 1
est un raccourci pour i = i + 1
.
i = 0
while i < 10:
print(i)
i += 1
print("Valeur de i :", i)
Comme indiqué, il existe différents types d'objets en python
. Sont présentés ici des exemples de créations et de manipulations de chaînes (str
), de tuples
, de list
et de dictionnaires (dict
).
Une chaîne de caractère se définit à l'aide des quotes simples (''
) ou doubles (""
). Par défaut, python
présentera les chaînes avec des simples quotes. Mais en présence d'une apostrophe dans la chaîne, il faut la déclarer avec des doubles quotes. Il est possible de connaître la longueur de la chaîne avec la fonction len()
.
"bonjour"
"aujourd'hui"
a = 'bonjour'
len(a)
Pour extraire des sous-chaînes, on utilise l'indexation en séquence python
, en prenant en compte que le premier caractère est en position 0
. La séquence par défaut est par pas de 1 (par exemple, 1:5
renvoie les positions 1
, 2
, 3
, 4
, 5
). Si on omet le premier ou le dernier, python
comprend qu'on désire le début ou la fin de la chaîne. On peut ajouter un paramètre à la séquence, permettant de jouer sur le pas entre les valeurs de la séquence.
a[1:5]
a[0:3]
a[:3]
a[3:len(a)]
a[3:]
a[::]
a[::-1]
a[::2]
a[1:5:2]
a[5:1:2]
Sur ces chaînes, on peut réaliser un certain nombre d'opérations classiques, telles que le changement de casse (upper()
ou lower()
), la mise en majuscule des premières lettres de chaque mot (capitalize()
), la recherche d'une sous-chaîne (find()
- première occurence), le remplacement d'une sous-chaîne (replace()
), le dénombrement de sous-chaînes (count()
) ou le découpage en sous-chaînes selon un caractère (split()
). En voici quelques exemples.
a.upper()
a.capitalize()
a.find('j')
a.replace('jour', 'soir')
a.count('o')
a.split('j')
Un tuple
en python
est un ensemble déclaré via des ()
, composé de valeurs pas forcément de même type et éventuellement complexe, qu'il n'est pas possible de modifier. C'est en quelque sorte une constante, une fois déclarée.
a = (3, 1, 9, 7)
print(a)
a[0]
a[0] = 5 # Pas possible : tuple == constante
Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.
Une list
est aussi un ensemble déclarée via des []
, composé d'éléments pas forcément tous du même type et possiblement complexe. A la différence d'un tuple, une liste est modifiable.
a = [3, 1, 9, 7]
print(a)
len(a)
a[0]
a[1:3]
Il est possible d'utiliser les mêmes outils d'indexation en séquence vu pour les chaînes.
Nous disposons sur ces listes de plusieurs fonctions tels que reverse()
(pour inverser la liste), sort()
(tri, avec l'option reverse
pour le choix du tri), pop()
(pour récupérer et supprimer le dernier élément), append()
(pour ajouter un élément à la fin), insert()
(pour insérer un élément dans la liste, à la position indiquée - paramètres = position suivie de la valeur), remove()
(pour supprimer les valeurs passées en paramètre). Toutes ces fonctions modifient directement la liste sur laquelle on les applique.
a.reverse()
print(a)
a.sort()
print(a)
a.sort(reverse=True)
print(a)
a.pop()
print(a)
a.append(5)
print(a)
a.insert(0, 6)
print(a)
a.remove(7)
print(a)
Un autre moyen d'insérer une valeur, voire plusieurs, à une liste est d'utiliser l'opérateur +
, tel qu'indiqué ci-dessous. Celui-ci permet une concaténation des deux listes en une seule. L'opérateur *
permet lui de répéter une liste autant de fois que désiré.
a + [1, 2]
a * 2
On peut utiliser un mécanisme spécifique, appelé list comprehension (fonctionnant aussi sur les chaînes et les tuples), permettant de récupérer les valeurs (ou un calcul sur chaque valeur) pour tous les éléments de la liste (ou certains si on applique un if
).
a = [3, 1, 9, 7]
[x**2 for x in a]
[x**2 for x in a if x >= 4]
Par contre, il faut faire très attention au passage de référence lorsqu'on copie une liste. En effet, dans le code suivant, on copie a
dans b
. Et en modifiant a
, on remarque que b
est aussi modifié. Et l'inverse est aussi vrai.
a = [1, 2, 3, 4]
print(a)
b = a
print(b)
a[0] = 5
print(a)
print(b)
b[1] = 9
print(b)
print(a)
Pour remédier à ce problème, on doit duppliquer la liste avec la fonction copy()
de la liste initiale, comme ci-dessous.
b = a.copy()
a[0] = -1
print(a)
print(b)
Les dictionnaires (dict
en python
) sont des listes nommées (définies via des {}
), c'est-à-dire que chaque élément a un nom (appelé aussi clé). Ces éléments ne sont pas forcément tous du même type, et peuvent aussi être complexe.
a = { "nom": "Jollois", "prenom": "FX", "langues": ["R", "Python", "SQL", "SAS"], "labo": { "nom": "LIPADE", "lieu": "CUSP"}}
print(a)
len(a)
Pour accéder aux éléments du dictionnaire, on peut utiliser le formalisme suivant.
a["nom"]
a["langues"]
a["langues"][0]
a["labo"]
a["labo"]["lieu"]
Il existe aussi des fonctions utiles sur ces objets, telles que get()
(pour récupérer la valeur d'une clé), keys()
(pour avoir la liste des clés de l'objet), values()
(pour avoir les valeurs des clés, dans le même ordre que listé dans keys()
), popitem()
(pour récupérer un dictionnaire avec le dernier item, et le supprimer du dictionnaire initiale) et pop()
(pour récupérer la valeur de l'item passé en paramètre, et le supprimer de l'élément de départ).
a.get("nom")
a.keys()
a.values()
a.popitem()
print(a)
a.pop("nom")
print(a)
On peut ajouter facilement un item à un dictionnaire, en lui affectant une valeur.
a["type"] = "MCF"
print(a)
De même que pour les listes, il faut faire attention lors de l'affectation d'un dictionnaire à un autre. La fonction copy()
permet donc d'obtenir une copie indépendante de l'objet initial.
b = a
b["prenom"] = "Xavier"
print(b)
print(a)
b = a.copy()
b["prenom"] = "FX"
print(b)
print(a)
Le mécanisme de list comprehension est aussi utilisable pour créer un dictionnaire. Il faut dans ce cas indiquer deux valeus : la clé et sa valeur. Dans notre cas, la fonction dict()
appliqué sur le résultat de la fonction zip()
des deux listes nous permet d'avoir le même résultat.
fruits = ["pommes", "bananes", "poires", "oranges"]
nombres = [5, 2, 10, 4]
{fruits[i]:nombres[i] for i in range(4)}
dict(zip(fruits, nombres))
L'opérateur def
permet de créer une fonction (ou une procédure qui sera juste une fonction ne renvoyant rien). L'opérateur return
indiquant le résultat à renvoyer le cas échéant. Le premier exemple est une fonction renvoyant une valeur (approximative) de $\pi$. Comme pour un if
, le bloc d'instructions est défini selon l'indentation.
def pi():
res = 3.141593 ** 2
return res
pi()
Ce deuxième exemple est une procédure affichant tout simplement "Bonjour"
.
def afficheBonjour():
print("Bonjour")
afficheBonjour()
Il est bien évidemment possible de passer un paramètre à une fonction, sans qu'on ait à déclarer son type. Bien sûr, un appel de la fonction sans valeur pour un paramètre défini entraîne une erreur.
def afficheBonjour(nom):
print("Bonjour", nom)
afficheBonjour("Jollois")
afficheBonjour()
Lorqu'il y a plus d'un paramètre, on peut faire un appel classique. Mais il est aussi possible de nommer explicitement les paramètres. Avec ce mécanisme, il est ainsi possible de les déclarer dans l'ordre que l'on veut. Mais si l'on nomme un paramètre, il est obligatoire de nommer les autres (erreur d'exécution sur la dernière ligne).
def afficheBonjour(nom, prenom):
print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
afficheBonjour(nom = "Jollois", prenom = "FX")
afficheBonjour(prenom = "FX", nom = "Jollois")
afficheBonjour(prenom = "FX", "Jollois")
Il existe la possibilité de définir une valeur par défaut à un paramètre dans une fonction. Ceci permet d'appeler la fonction sans donner de valeur pour ce paramètre (la fonction utilisera celle par défaut donc).
def afficheBonjour(nom, prenom = "?"):
print("Bonjour", prenom, nom)
afficheBonjour("Jollois", "FX")
afficheBonjour("Jollois")
Une fonction est dite pure si elle n'a pas d'effet de bords lors de son appel. Dans la fonction f1()
définie ci-dessous, le paramètre a
est local et son affectation ne change pas la valeur de la variable a
globale. f1()
sera donc considérée comme pure.
def f1(a):
a = [b**2 for b in a]
return a
a = list(range(5))
print(a)
print(f1(a))
print(a)
Maintenant, si nous définissons la fonction comme f2()
ci-dessous, nous remarquons que la variable a
globale est modifiée suite à l'appel de la fonction. f2()
est considérée impure.
def f2(a):
for i in a:
a[i] = a[i] ** 2
return a
a = list(range(5))
print(a)
print(f2(a))
print(a)
Il arrive régulièrement que l'on doive gérer les erreurs d'exécution dans une fonction. On utilise pour cela l'opérateur try
, qui contiendra le code à exécuter normalement. Et dans le bloc except
, on indiquera la marche à suivre en cas d'erreur lors de l'exécution du try
. On peut définir une suite d'instruction à réaliser après la gestion de l'erreur avec un bloc finally
(optionnel).
Ci-dessous, nous définissons une fonction renvoyant la somme d'une liste ou d'un tuple. Si la somme est impossible à réaliser (par exemple, car il y a une chaîne dans la liste passée en paramètre), on affiche un message d'erreur et on renvoie la valeur None
.
def somme(v):
try:
res = sum(v)
except:
print("Erreur : somme impossible !")
res = None
finally:
return res
a = somme([1, 3, 5])
print(a)
a = somme(["un", 3, 5])
print(a)
Dans le cadre de l'utilisation de liste, il est nécessaire d'utiliser des fonctions particulières, dites high order functions. Ces dernières prennent en paramètre une fonction et une liste.
Pour les exemples, nous définissons une liste de nombres.
a = [13, 7, 2, 9, 1, 10, 6, 3, 8]
print(a)
map()
¶La fonction map()
permet d'appliquer une fonction sur chaque élément d'une liste (ou d'un tuple). Elle renvoie un objet de type map
que l'on peut transformer en liste avec la fonction list()
. Une fois celle-ci appliqué, l'objet renvoyé par map()
est vide.
Ici, nous définissons une fonction carre()
qui renvoie le carré du paramètre passé. Et nous l'appliquons sur chaque élément de la liste a
créé précédemment.
def carre(v):
return v ** 2
carre(5)
b = map(carre, a)
print(list(b))
filter()
¶L'idée de la fonction filter()
est de filtrer les valeurs de la liste, en fonction du résultat de la fonction passée en paramètre. De même que pour map()
, nous listons les résultats pour l'avoir sous une forme de list
.
La fonction pair()
ci-dessous teste si la valeur passée en paramètre est divisible par 2 (donc si elle est paire). L'utlisation de la fonction filter()
sur a
, avec cette fonction, nous renvoie les valeurs de a
paires.
def pair(v):
return v%2 == 0
pair(5)
pair(8)
b = filter(pair, a)
print(list(b))
reduce()
¶La dernière fonction intéressante sur les listes est reduce()
, permettant de réduire une liste en une valeur. Elle est contenue dans le module functools
, qu'on importe via la commande import
.
La fonction à passer en paramètre doit donc prendre deux valeurs et renvoyer une valeur de même type. Le deuxième paramètre est la liste sur laquelle appliquer la réduction. Il est aussi possible d'ajouter une valeur initiale.
Cette fonction reduce()
regroupe deux éléments de liste ensemble, puis regroupe le résultat précédent avec l'élément suivant, et ainsi de suite, jsuq'à épuisement de la liste.
Voici un exemple d'utilisation de reduce()
pour le calcul de la somme des éléments d'une liste. On définit la fonction somme()
qui prend deux éléments et qui renvoie la somme de deux. On l'applique, via reduce()
sur la liste. Le résultat obtenu est bien la somme sur a
. Le deuxième appel est avec une valeur initialisée à 100.
def somme(v1, v2):
return v1 + v2
somme(3, 10)
sum(a)
import functools
b = functools.reduce(somme, a)
print(b)
b = functools.reduce(somme, a, 100)
print(b)
Dans ces appels de fonctions, il est courant de passer en paramètre une fonction simple. Dans ce cadre, il existe un mécanisme évitant la déclaration de la fonction avant. On parle de fonction anonyme, déclarée via l'opérateur lambda
. Le principe est de définir la fonction lors de son passage comme paramètre. La contrainte est que cette fonction doit tenir sur une seule ligne.
Dans l'exemple qui suit, nous appliquons une fonction calculant le carré d'une valeur à une liste, via la fonction map()
.
b = map(lambda v: v ** 2, a)
print(list(b))
Il est possible d'ajouter une condition if
dans la fonction lambda
avec un formalisme de type valeurTrue if condition else valeurFalse
. Ci-après, nous calculons le carré de chaque valeur, multiplié par -1
pour celles inférieur ou égale à 8.
b = map(lambda v: v **2 if v > 8 else -(v ** 2), a)
print(list(b))
Le module os
, importé ci-dessous, dispose de plusieurs routines dédiés à la gestion des fichiers.
import os
Comme dans tout langage, il est possible d'accéder aux fichiers de deux façons : soit par adressage absolu (chemin complet du fichier), soit par adressage relatif (chemin par rapport à un répertoire). Pour la deuxième solution, il est donc important de connaître la notion de répertoire courant dans python
. Celui-ci est le répertoire à partir duquel python
va chercher les fichiers (script, de données ou autres).
# connaître
os.getcwd()
# changer
os.chdir("donnees")
os.getcwd()
La fonction open()
permet d'ouvrir une connexion vers un fichier (en mode lecture seule par défaut). L'objet renvoyé contient une fonction de lecture de toutes les lignes en une fois (readlines()
). Celle-ci renvoie une liste de chaînes de caractères, chaque élément représentant une ligne du fichier. Il faut bien évidemment fermer la connexion au fichier, avec la fonction close()
.
Voici comment lire les lignes du fichier Iris.txt en python
.
fichier = open("Iris.txt")
lignesBrutes = fichier.readlines()
fichier.close
Le fichier contient les informations de 150 iris, répartis en 3 espèces et décrits par 4 variables. Voici quelques informations sur ce fichier.
lignesBrutes[0:5]
len(lignesBrutes)
python
comme ci-dessus";"
). Supprimer les quotes ("'"
) et le caractère de fin de ligne ("\n"
).f(1.234)
renverra 1.234
, mais f("rien")
renverra "rien"
setosa
.