Mémo Python pour la Data Science

Mémo Python pour la Data Science synthétisant mes notes personnelles lorsque j’ai commencé.

Partie 1 : les variables

Quelles sont les types de variables ?

Python est un langage sensible à la case. Autrement dit, quand on définit le nom des variables, on doit faire attention aux majuscules et minuscules.

La convention veut que les noms de variables commencent toujours par une minuscule et jamais par un chiffre.

Enfin, on ne peut pas créer de variables avec comme nom un mot clé :

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield]

(J’ai utilisé keyword.kwlist pour obtenir cette liste).

Parmi les nombres, on distingue les int (sans virgule) et les float (avec virgule). Sont très utilisés les bool qui équivalent à soit True ou False.

Comment connaître le type d’une variable ? 

Comme son nom l’indique, la fonction type(…) renvoie le type de la variable passée en paramètre :

theme = 'sport'
type(theme)

On obtient :

<class 'str'>

Comment convertir une variable ?

Une variable peut être convertie en un autre type via des fonctions existantes :

Utiliser la fonction int() pour modifier une variable en entier.

theme_int = int(theme)

La fonction float() pour modifier une variable en nombre à virgule.

theme_float = float(theme)

La fonction str() pour modifier une variable en chaîne de caractère.

theme_str = str(theme)

Remarque :

Depuis le début j’écris des variables contenant des chaînes de caractères avec des apostrophes mais il est possible d’utiliser des guillemets :

my_word = 'this'

ou

my_word = "this"

La différence essentielle est que si je veux stocker des apostrophes dans ma chaînes de caractères, dans le premier cas ça ne va pas fonctionner parce que le programme croit que la chaîne s’arrête au premier apostrophe.

Une solution est d’utiliser des guillemets ou un caractère d’échappement. Parce que la situation sera la même si je veux utiliser des guillemets dans la solution 2. Libre à vous de choisir.

En tout cas, utiliser des guillemets par moment et des apostrophes à d’autres moments, ce n’est pas bien !

Dans le même esprit, pour écrire des chaînes de caractères sur plusieurs lignes, on peut même utiliser 3 apostrophes :

my_big_word = '''Hello
content
Bye'''

Avec les chaînes de caractères, on peut aussi extraire des morceaux de la chaînes de caractère:

my_word = 'this'
print(my_word[1])

En fait voilà comment ça fonctionne, chaque lettre dans ma chaîne a un indice

Ainsi, in peut récupérer du caractère 0 à 2 (exclu).

print(my_word[1:4])

Du caractère 1 jusque la fin

print(my_word[1:])

le dernier caractère

print(my_word[-1])

l’avant dernier caractère

print(my_word[-2])

Et ainsi de suite.

Partie 2 : les opérateurs

Les types d’opérateur

Grosso modo, il y a deux types de nombre :

  1. les nombre digitaux, sans virgules
  2. les nombres à virgule

Sur ces nombres on peut bien sûr appliquer les opérations élémentaires, à savoir

  • l’addition
result = 20+32
  • la soustraction
result = 67-1
  • la multiplication
result = 2*60
  • la division
result = 10/2
  • la puissance
result = 5**2

Petite astuce pour la division : si je veux récupérer un nombre entier je mets 2 slashs.

result = 10//2

Le pourcentage va lui nous donner le reste de la division

result = 10%2

Pour incrémenter un nombre,  la méthode naïve est de procéder comme suit :

result = result +3

Mais  il existe aussi une petite astuce syntaxique :

result += 3

Ce qui fonctionne aussi pour décrémenter :

result -= 3

Les priorités

Attention à la priorité dans les opérations. La multiplication a la priorité. Par exemple :

result = 10+3*2

3 *2 est d’abord traité

Python suit les conventions mathématiques mais il existe tout de même un moyen mnémotechnique pour s’en souvenir : l’acronyme PEMDAS :

  • P pour parenthèses qui a la priorité la plus forte.
  • E pour exponentiation qui est le second niveau de priorité. 
  • M pour multiplication.
  • D pour division. Multiplication et division ont le même niveau de priorité. Idem pour Addition et Soustraction. Donc comme en maths, dans ce cas le traitement s’effectue de gauche à droite.

Quoi qu’il arrive, lorsqu’on n’est pas sûr de la priorité, on utilise des parenthèses. Cela facilite aussi la lecture ! 😉

Partie 3 : les collections

Qu’est-ce qu’une liste en Python ?

Une liste est une séquence de valeurs. Cette séquence est entourée par des crochets :

[18, 23, 09]

Les éléments de la liste ne sont pas nécessairement du même type :

['text', True, 2020]

On peut aller plus loin en ayant une liste comme élément d’une liste :

['text', True, 2020, ['text', True, 2020]]

Accéder à un élément d’une liste

Définissons une liste de couleurs :

colors = ['yellow', 'green', 'red']

On peut accéder à un élément d’une liste via l’indice. C’est-à-dire le numéro de l’élément dans la liste, en sachant que le premier élément est numéroté zéro.

print(colors[1])

Résultat :

green

Modifier un élément d’une liste

Les listes sont mutables, c’est-à-dire qu’on peut modifier les valeurs. Ceci est rendu possible en utilisant des crochets ainsi qu’un indice pour identifier la valeur à modifier :

my_var[1] = 'new text'

La nouvelle liste est :

['text', 'new text', 2020, ['text', True, 2020]]

Supprimer un élément d’une liste

Il existe plusieurs manières de supprimer une liste.

  • avec .pop() si on connaît l’indice
my_list = ['text', 'new text', 2020, ['text', True, 2020]]
value = my_list.pop(1)
print(value)
> new text

print(my_list)
> ['text', 2020, ['text', True, 2020]]

La liste est modifiée et pop retoure l’élément qui a été retiré de la liste.

Sans préciser l’indice, il supprime le dernier élément de la liste :

my_list = ['text', 'new text', 2020, ['text', True, 2020]]
value = my_list.pop()
print(value)
> ['text', True, 2020]

print(my_list)
> ['text', 'new text', 2020]
  • avec del si on n’a pas besoin de la valeur supprimée
my_list = ['text', 'new text', 2020, ['text', True, 2020]]
del my_list[0]
print(my_list)
> ['new text', 2020, ['text', True, 2020]]

del permet de supprimer une slice de la liste (le second indice exclu) :

my_list = ['text', 'new text', 2020, ['text', True, 2020]]
del my_list[0:2]
print(my_list)
> [2020, ['text', True, 2020]]
  • avec .remove() si tu connais l’élément à supprimer mais pas son indice
my_list = ['text', 'new text', 2020, ['text', True, 2020]]
my_list.remove('text')
print(my_list)
> ['new text', 2020, ['text', True, 2020]]

remove() retourne None. 

Manipuler des listes

L’opérateur ‘+’ concatène des listes :

f_list = ['text', 'new text', 2020, ['text', True, 2020]]
s_list = [4, 5, 6]
global_list = my_f_list + my_s_list
print(global_list)
> ['text', 'new text', 2020, ['text', True, 2020], 4, 5, 6]

L’opérateur ‘*’ répète la liste autant de fois qu’il est indiqué. Utile pour initialiser la liste, par exemple :

l = [0] * 10
print(l)
> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Utiliser les slice est très courant pour manipuler les listes :

l = [1, 2, 3, 4, 5, 6]
print(l[1:3])
> [2, 3]
print(l[:4])

[0, 1, 2, 3, 4]
print(l[3:])
> ['d', 'e', 'f']

Ajouter un élément à la fin de la liste avec .append() :

l = [1, 2, 3]
l.append(4)
print(l)
> [1, 2, 3, 4]

L’élément ajouté est ajouté tel quel à la liste de départ :

l = [1, 2, 3]
l.append([4, 5, 6])
print(l)
> [1, 2, 3, [4, 5, 6]]

Tout comme append(), .extend() ajoute des éléments à une liste. Mais l’élément ajouté est ajouté de manière à garder la forme de la liste de départ :

l = [1, 2, 3]
ll = [4, 5]
l.extend(ll)
print(l)
> [1, 2, 3, 4, 5]

Si on veut ajouter au milieu, on utilise .insert() :

 l.insert(0, 'xxx')

où 0 est l’indice dans la liste.

.pop() pour retirer le dernier élément de la liste :

l = [1, 2, 3]
l.pop()
print(l)
> [1, 2]

.index pour vérifier l’existence d’un élément (la première occurrence). Il retourne l’index. Mais c’est plus propre d’utiliser le mot clé in :

if 50 in l:
    print('OK')

.sort() ordonne la liste

l = ['d', 'c', 'e', 'b', 'a']
l.sort()
print(l)
> ['a', 'b', 'c', 'd', 'e']

.count() ou encore .copy() sont également souvent utilisés.

Les listes à deux dimensions pour la Data Science

En Data Science on travaille énormément avec des matrices. Qu’est-ce qu’une matrice ? Ni plus ni moins qu’un tableau à deux dimensions. Qu’est-ce qu’un tableau à deux dimensions ? Un tableau de tableaux. Et un tableau est une liste. Donc on utilise les listes à deux dimensions pour représenter une matrice.

Par exemple pour la matrice 3×3 suivante :

On a en Python :

matrix = [[3, 4, 5],[5, 9, 7]]

Pour accéder une liste 2D, on utilise les crochets comme pour une liste classique mais puisqu’il s’agit d’une liste de listes, on utilise deux paires :

print(matrix[1][0])
> 5

L’indice dans le premier crochet représente le numéro de ligne, le second le numéro de colonne.

De ce fait, en l’absence de la seconde paire de crochet, l’ensemble de la ligne sera concernée :

print(matrix[1])
> [5, 9, 7]

Contrairement aux listes, les dictionnaires sont utilisés lorsqu’on a des paires d’informations. Une clé et une valeur. Par exemple, si j’ai l’âge d’Hubert. Son nom est la clé et la valeur est Hubert.

user = { 
	"name":"Hubert",
	"age":90
}

Chaque clé une unique, par exemple je ne peux PAS avoir :

user = { 
	"name":"Hubert",
	"age":90,
	"age":67
}

De cette manière je peux accéder à la valeur du dictionnaires via la clé (et non un indice comme un liste) :

print(user["age"])
> 90

Mais la valeur peut être une liste et pour accéder à un élément de cette liste, je vais me servir des crochets avec l’indice de l’élément dans la liste :

visitor = { 
        "name":["Hubert", "Giscard"],
	"age":90
}
print(visitor["name"][0])
> Hubert

Logiquement si j’accède à une clé du dic qui n’existe pas, je génére une erreur

print(user["city"])
>Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'city'

Idem si je ne respecte pas la case

print(user["Age"])
>Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Age'

Pour éviter de générer une erreur, il faut utiliser .get().

print(user.get("City"))
> None

Il existe une multitude de méthodes utiles, celles que j’utilise le plus :

.keys() qui retourne les clés

print(visitor.keys())
> dict_keys(['name', 'age'])

.values() qui retourne les valeurs

print(visitor.values())
> dict_values([['Hubert', 'Giscard'], 90])

.items() qui retourne les éléments

dict_items([('name', ['Hubert', 'Giscard']), ('age', 90)])
> dict_items([('name', ['Hubert', 'Giscard']), ('age', 90)])

.update() qui met à jour le dictionnaire

print(visitor.update({"age":50}))
> None
print(visitor)
{'name': ['Hubert', 'Giscard'], 'age': 50}

.pop() qui retire l’élément

print(visitor.pop('age'))
> 50
print(visitor)
{'name': ['Hubert', 'Giscard']}

Créer un tuple

À l’image des listes listes, les tuples servent à stocker des données. La valeur des éléments peut être de n’importe quel type et ces valeurs sont indexées par indice comme pour les listes. Mais les tuples ont la particularité de ne pas être modifiables. On dit qu’ils sont immuables).

Voici une liste :

countries = ['France', 'Spain']

Voici un tuple :

countries = ('France', 'Spain') 

On utilise des crochets pour les listes et des parenthèses pour les tuples. Les parenthèses ne sont pas obligatoires :

countries = 'France', 'Spain'

Mais c’est conseillé pour la lisibilité :

countries = ('France', 'Spain')

On crée un tuple avec des parenthèses :

my_t = ()

Il est possible d’utiliser la fonction toute faite pour créer un tuple :

my_t = tuple()

Ajouter une valeur

Pour ajouter un élément :

my_t = (1,)

Je mets une virgule parce qu’il n’y a qu’une valeur, sans quoi ce ne sera pas un tuple. Le but du tuple étant d’avoir un ensemble de valeurs :

my_t = (1)
type(my_t)
> <class 'int'>

my_t = (1,)
type(my_t)
<type 'tuple'>

Le tuple est une sorte de liste, on peut donc utiliser la même syntaxe pour lire les données du tuple :

my_t[0]
> 1

Puisqu’il est immuable, il n’est pas possible de modifier une valeur :

my_t[0] = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Son avantage réside dans le fait de pouvoir jouer avec plusieurs valeurs en même temps :

my_t = (1, 2, 3)
f_val, s_val, t_val =  my_t
print(f_val)
> 1
print(s_val)
> 2
print(t_val)
> 3

C’est ce qu’on appelle l’unpacking.

L’unpacking

L’unpacking permet de gagner en lisibilité. Pour ce faire, le nombre de variables pour réceptionner les valeurs unpacked du tuple doit être égal au nombre d’éléments dans le tuple :

my_t = (1,)
val_t = my_t
print(val_t)
> (1,)

my_t = (1, 2)
f_val, s_val = my_t
print(f_val)
> 1

my_t = (1, 2, 3)
f_val, s_val = my_t
> ValueError: too many values to unpack (expected 2)

La fonction toute faite enumerate permet de parcourir les éléments :

for i, el in enumerate(my_t):
    print(i, el)

Une petite astuce : les fonctions retournent normalement qu’une valeur mais grâce aux tuples une fonction peut retourner plusieurs valeurs :

values = [56, 78]

def my_func(f_param, s_param):
     return f_param + s_param, s_param

total, s_param = my_func(values)

De plus, je peux appeler cette fonction de manière classique :

my_func(values[0], values[1])

Ou avec l’opérateur magique splat (l’astérisque) :

my_func(*values)

Sans astérisque, le message d’erreur nous fait comprendre que le premier paramètre correspond au tuple et qu’il manque des valeurs pour les autres paramètres :

my_func(values)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: my_func() missing 1 required positional argument: 's_param'

En revanche, il est tout à fait possible de mettre moins d’arguments et préciser les autres manuellement comme suit :

my_func(10, *stuplet)
> 16

Cette technique est utilisable également pour une usage simple :

my_t = (1, 7, 7, 5, 90)
f, *s = my_t
f
> 1
s
[7, 7, 5, 90]

f, *s, t = my_t
f
> 1
s
> [7, 7, 5]
t
> 90

Afin d’éviter de stocker une valeur sans s’en servir, l’underscore permet d’ignorer littéralement la valeur :

total, _ = my_func(values)

Partie 4 : Les boucles

Les boucles

Utiliser des listes ou des dictionnaires pour stocker des données, c’est bien. Il arrive cependant qu’une instruction soit répétée dans le programme. Pour ça on va utiliser le mécanisme de boucles. Cela permettra d’exécuter plusieurs fois du code sans avoir à l’écrire plusieurs fois tranquille pépère.

Dans les scripts de scraping (récupérer des informations sur une page web), par exemple, on boucle souvent pour parcourir la page web et à chaque étape faire des vérifications. Est-ce que cet élément de la page dispose de telle information ?

Il existe deux boucles :

  1. While
  2. For

On utilise une boucle Whilequand on ne sait pas combien de fois on doit répéter l’instruction mais qu’on connaît la condition d’arrêt. Concrètement ça se traduirait en langage par : répète ce code jusqu’à ce que tu trouve le nom de la personne sur la page.

On ignore combien de fois est-ce qu’on devra répéter le code.

Tandis qu’avec la boucle For on connaît à l’avance le nombre d’itérations.

En code ça donne :

i = 0
while i < 100:
    // checkt
    i += 1

La variable i est un compteur pour compter le nombre de tours et ainsi constituer la condition d’arrêt. Sans quoi la boucle se répétera indéfiniment.

En ce qui concerne la boucle For :

my_list = [2, 356, 65, 0, 2] 
   
for i in my_list: 
    print(i) 

On utilise une variable à chaque tour de boucle : la variable i stocke le contenu de l’itération.

La fonction toute faite range permet d’alléger la boucle :

for i in range(10):
    print(i)

range génère une suite arithmétique allant de 0 à 10 (non compris).

for i in range(8, 10)
    print(i)
> 8, 9

for i in range(3, 10, 3)
    print(i)
> 3, 6, 9

range permet de préciser la valeur de départ et le pas pour faire progresser l’itérateur.

Bien sûr on peut boucler sur un dictionnaire et récupérer la clé et la valeur via la méthode .items() !

Pour rappel : les dictionnaires sont constitués de paires d’information. Une clé et une valeur. Par exemple, si j’ai l’âge et le nom d’Hubert. Son nom est la clé et la valeur est Hubert.

my_dic = {'name': 'Hubert', 'age': 4}
for i, v in my_dic.items():
    print(i, v)

Idem pour lists, tuples ou même les string avec la fonction .enumerate() :

for i, v in enumerate([4, 90, 67, 0, 7]):
    print(i, v)

Le concept de boucles imbriquées permet de boucler sur deux séquences en même temps :

for x in range(6):
    for y in range(8):
        print(f'({x}, {y})')
> (0, 0)
> (0, 1)
> (0, 2)
> (0, 3)
> (0, 4)
> (0, 5)
> (0, 6)
> (0, 7)
> (1, 0)
> (1, 1)
> (1, 2)
> (1, 3)
> (1, 4)
> (1, 5)
> (1, 6)
> (1, 7)
...

La fonction .zip() permet de boucler sur deux séquences directement :

first_list = [4, 90, 67, 0, 7]
second_list = [4, 90, 67, 0, 7]
for x, y in zip(first_list, second_list):
    print('First: {0}. Second: {1}.'.format(x, y))
> First: 4. Second: 4.
> First: 90. Second: 90.
> First: 67. Second: 67.
> First: 0. Second: 0.
> First: 7. Second: 7.

Et la plus fun de toutes, la fonction .reversed() qui permet de boucler en sens inverse :

for i in reversed(range(6)):
    print(i)
> 5
> 4
> 3
> 2
> 1
> 0

Mais en réalité la fonction souvent utilisée est .sorted() qui permet d’ordonner la séquence :

list = [4, 90, 67, 0, 7]
for i in sorted(set(list)):
    print(i)
> 0
> 4
> 7
> 67
> 90

Les conditions quésaco ?

Je veux faire quelque chose si machintruc sinon je ferai machinchouette.

Par exemple, si la partie est terminé j’informe joueur sinon j’affiche que la partie continue :

if game_over is True:
    print(f'GAME OVERT. Thank you {name} for playing')
else: 
    print('In progres')

Si la première condition n’est pas validée alors on passe dans le else.

Dans le cas où souhaiterait tester si la partie n’est pas terminée mais qu’il n’y a plus suffisamment de temps, le mot clé elif permet d’ajouter un cas :

if game_over is True:
    print(f'GAME OVERT. Thank you {name} for playing')
elif timer==0:
    print(f'GAME OVERT. Thank you {name} for playing')
else: 
    print('In progres')

Le plus élégant reste de multiplier les conditions sur une ligne :

if game_over is True or timer==0:
    print(f'GAME OVERT. Thank you {name} for playing')
else: 
    print('In progres')

Dans le cas où il n’y a qu’une instruction à exécuter, il est conseillé de n’utiliser qu’une ligne :

print('GAME OVERT') if game_over is True or timer==0 else print('In progres') 

Attention il doit obligatoirement y avoir au moins une instruction !

Si pour une raison ou pour une autre vous n’avez rien à mettre, utilisez le mot clé pass

if game_over is True or timer==0:
    pass
else: 
    print('In progres')

En résumé, il faut bien garder à l’esprit les opérateurs de comparaison disponibles, à savoir :

  • Si la variable est supérieure/inférieure strictement
if timer < 100:
   print('Go go go!')
else:
   print('Don't worry')

if timer > 100:
   print('Don't worry')
else:
   print('In progres')
  • Si elle est supérieure/inférieure ou égal
if timer <= 100:
   print('Go go go!')
else:
   print('Don't worry')

if timer >= 100:
   print('Don't worry')
else:
   print('In progres')
  • Dans le cas où elle est égale
if timer == 100:
   print('Go go go!')
else:
   print('Don't worry')
  • Ou même différente
if points != 100:
   print('Not yet!')
else:
   print('Perfect!')

Ou

if points <> 100:
   print('Not yet!')
else:
   print('Perfect!')

Enfin, les opérateurs logiques permettent de combiner plusieurs conditions :

  • and
if points == 100 and timer >100:
   print('Amazing!')
  • or
if points == 100 or timer >100:
   print('Not bad')

Partie 5 : Manipuler les chaînes de caractères

Exemples

Quelques méthodes utilisées pour travailles sur les chaînes de caractères et ainsi préparer les données :

  • len()

Cette fonction retourne la longueur d’un objet donné :

sentence = 'The random sentence'
print(len(sentence))

Résultat :

19

C’est utile pour, par exemple, vérifier qu’une donnée ne dépasse pas un nombre de caractères lors de la préparation de données.

  • upper() et lower()
sentence = 'The random sentence'
print(sentence.upper())
print(sentence.lower())

Résultat :

THE RANDOM SENTENCE
the random sentence

Ça peut être intéressant pour comparer du texte. Ainsi, on s’assure de standardiser le texte en majuscule ou minuscule.

  • find()
sentence = 'The random sentence'
print(sentence.find('t'))
print(sentence.find('T'))
print(sentence.find('b'))
print(sentence.find('sentence')) 

Résultat :

14
0
-1
11

L’indice est retourné. À noter que find() retourne -1 s’il ne trouve pas la valeur.

  • replace()
sentence = 'The random sentence'
print(sentence.replace('random', 'magic'))

Résultat :

The magic sentence

Pour remplacer une sous-chaîne. Par exemple, lors de nettoyage de données pour remplacer les caractères mal encodés.

  • count()
sentence = 'The random sentence'
word = 'random'
print(sentence.count(word))

Résultat :

1

Compte le nombre de sous-chaînes.

  • capitalize()
sentence = 'the random sentence'
print(sentence.capitalize())

Résultat :

The random sentence

Convertit la première lettre en majuscule.

  • strip()
sentence = '     The r  andom sentence  '
print(sentence.strip())

Résultat :

The r  andom sentence

Enlève les espaces au début et à la fin.

  • startswith() et endswith()
sentence = 'The random sentence'
print(sentence.startswith('The'))

Résultat :

True

(Attention ces fonctions ne modifient pas la chaîne de caractères originale. Elles effectuent le traitement sur une copie temporaire).

Sélectionner une sous-chaîne

Les crochets permettre de sélectionner une sous-chaine via des indices indices. Le premier indice indique la valeur où de début dans la chaîne et la seconde la valeur de fin (non comprise). En sachant que les indices commencent à zéro.

sentence = 'The random sentence'
print(sentence[1:3])
print(sentence[0:3])
print(sentence[:3])
print(sentence[0:])
print(sentence[0:-1])

Résultat :

he
The
The
The random sentence
The random sentenc

Parcourir une chaîne

La structure for..in permet de parcouvrir une chaîne

sentence = 'The random sentence'
for c in sentence:
    print(c)

Résultat :

T
h
e
 
r
a
n
d
o
m
 
s
e
n
t
e
n
c
e

L’instruction in peut être utilisée sans le for de manière à tester la présence d’une chaîne dans une autre :

sentence = 'The random sentence'
c = 'T'
if c in sentence:
    print(c)

Résultat :

T

Formatter des Strings

Voyons la génération dynamique de texte avec des variables. Autrement dit, créer une phrase avec des valeurs :

print(f’Hi  {name}')

Partie 6 : Les fonctions

Les fonctions

Afin de pouvoir maintenir son code dans le temps, on crée des blocs de code réutilisable, c’est ce qu’on appelle les fonctions.

Une fonction est un bloc qui contient quelques lignes de codes pour exécuter une tâche spécifique.

Par exemple, plutôt que de vérifier plusieurs dans le code la même chose : si l’utilisateur est un admin. Il est plus judicieux d’écrire ce schmilblick une fois puis de le placer dans une fonction :

def my_function():
    // Do some stuff
    print("Hello Admin!")

Le mot clé def indique que c’est une fonction.

Le nom de la fonction est my_function.  Les règles pour le nommage de fonction sont les mêmes que les variables, c’est-à-dire que des lettres, des chiffres ou certains signes de ponctuation. Le premier caractère ne peut pas être un chiffre. Vous ne pouvez pas utilisez un mot clé comme nom de votre fonction. Et il est recommandé de ne pas avoir une fonction portant le même nom qu’une variable existante dans le programme. 

Pour rappel, voici la liste des mots clés en Python :

import keyword
print(keyword.kwlist)
> ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

Enfin, les parenthèses permet de préciser les arguments. En l’occurrence la fonction ne prend pas d’arguments.

Attention ! Le code à l’intérieur de la fonction ne sera pas exécuté lors de la lecture du code. Pour ça il faut faire un appel à la fonction comme suit :

def my_function():
    // Do some stuff
    print("Hello Admin!")

my_function()

Les paramètres

Le grand intérêt va être de passer des informations à notre fonction, pour ça on utilise les paramètres :

def my_function(name):
    print(f'{name}')

Attention, l’ordre des paramètres compte. Par exemple :

def my_function(name, age):
    print(f'name: {name}')

my_function(23, 'toto')

Le résultat :

name: 23

Pour prévenir éviter la petite boulette, il peut être malin d’utiliser des mots clés :

def my_function(name, age):
    print(f'name: {name}')

my_function(age=23, name='toto')

Le résultat :

name: toto

La valeur de retour

Par défaut la fonction retourne None (c’est un object qui signifie l’absence de valeur). Dans l’exemple ci-dessus le None est écrasée par le print. Mais, dans cet exemple, rien n’écrase le None :

def my_function(name, age):
    new_age=20+age

value = my_function(age=23, name='toto')
print(value)
print(type(value))

Résultat :

None
<class 'NoneType'>

Dans le cas où on souhaiterait retourner une valeur, on peut indiquer explicitement ce qu’on souhaite retourner avec le mot clé return.

def my_function(name, age):
    new_age=20+age
    return new_age

value = my_function(age=23, name='toto')
print(value)
print(type(value))

Résultat :

43
<class 'int'>

La portée des variables

Une variable dans une fonction est dite locale. C’est-à-dire qu’elle existe uniquement dans la fonction.

Par exemple :

def my_function(new_score):
    score = new_score+100
    return score

new_score = score+1

Résultat :

NameError: name 'score' is not defined

Il m’est impossible d’utiliser la variable score en dehors de la fonction. Seule la valeur retournée par la fonction est utilisable :

def my_function(new_score):
    score = new_score+100
    return score

new_score = my_function(1)
print(new_score)

Résultat :

101

Les fonctions mathématiques

Python a un module dédié aux fonctions mathématiques. Ce qui permet d’accéder à un grand nombre de fonctions.

Pour y avoir accès, il suffit de l’importer :

import math

Ainsi, je peux avoir accès aux fonctions du module en utiliser l’objet importé math suivi d’un point et du nom de la fonction :

radians = 0.6
height = math.sin(radians)

Ou encore :

math.pi 

Résultat :

3.141592653589793

Les modules

Python fourni deux façons d’importer des modules :

Premièrement il nous est possible d’importer le module et d’accéder aux fonctions via ce module :

import math
print math.pi
3.14159265359

Réusltat :

3.14159265359

De cette façon, il n’est pas possible d’accéder à une fonction du module sans préciser son nom :

print(pi)

Résultat :

NameError: name 'pi' is not defined

Une autre manière est  de préciser la fonction qu’on souhaite utiliser dans l’import :

from math import pi

Bye bye l’erreur, tout va bien maintenant :

print(pi)

Résultat :

3.14159265359

Évidement il n’est pas pratique d’avoir à préciser toutes les fonctions dans l’import. Heureusement il y a l’opérateur magique, l’astérisque pour importer directement toutes les fonctions du module :

from math import *

Attention cela dit à cette dernière technique parce que cela peut créer des conflits de naming avec vos variables. On ne sait pas tout ce qu’on import.

Enfin, on va terminer avec un petit conseil maison :

it is a good idea to ensure that every possible path through the program hits a return statement. For example:

def absolute_value(x):
    if x < 0:
        return -x
    if x > 0:
        return x

Cette fonction n’est pas bien. C’est toujours une bonne idée de s’assurer que tous les cas soient traités.

En résumé c’est très important de diviser son programme en fonctions car

  • le programme est plus facile à lire, à comprendre
  • ça permet d’éviter la duplication de code donc alourdi inutilement
  • le code générique est favorisé donc réutiliseable
  • le debuggage est simplifié : il n’y a juste qu’à changer à un endroit

Partie 7 : La compréhension de listes

Comment optimiser son code ?

Par optimiser j’entends rendre le code plus succincte et lisible.

Petit rappel de la boucle FOR de base :

my_seq = [5, 3, 0]
for e in my_seq:
    print(e)

Résultat :

5
3
0

Combien de fois j’ai créé une liste à partir des données d’une autre liste parce qu’il y a une phase intermédiaire de nettoyage, par exemple :

my_seq = [5, 3, 0]
new_seq = []
for e in my_seq:
    ee = e + 1
    // clean and transform
    new_seq.append(ee)
print(new_seq)

Résultat :

[6, 4, 1]

Le principe de compréhension de liste permet de faire ce traitement plus efficacement :

my_seq = [5, 3, 0]
new_seq = [e + 1 for e in my_seq]

Construction d’une compréhension de liste :

result = [element for element in list if condition(element)]

Le point très intéressant c’est de pouvoir faire ce que bon nous semble. Par exemple, transformer les éléments de ma liste ma liste en chaîne de caractère :

my_seq = [5, 3, 0]
result = [str(e) for e in my_seq]
print(result)

Résultat :

['5', '3', '0']

Enfin, la condition permet d’opérer un filtrage sur cette liste :

my_seq = [5, 3, 0]
result = [e for e in my_seq if e > 2]
print(result)

Résultat :

[5, 3]

Partie 8 : Les fichiers

Il n’est pas rare de se retrouve avec des fichiers dans lesquels sont stockés les informations.

Ouverture

La fonction open permet d’ouvrir un fichier :

file = open('my_file.csv', 'r')
content = file.readlines()
file.close()

Le second paramètre correspond au mode d’ouverture :

  • r : mode lecture
  • w : mode écriture (si le fichier n’existe pas il est créé)
  • a : mode écriture à la fin du fichier (si le fichier n’existe pas il est créé)
  • b : mode binaire
  • t : mode texte
  • x : mode pour créer un nouveau fichier

Le problème de cette méthode pour ouvrir un fichier est qu’il est ne faut pas oublier de fermer le fichier. Le mot clé with permet de ne pas avoir à fermer le fichier. Il se chargera de détecter automatiquement quand fermer le fichier :

with open('my_file.csv','r') as file
    content = file.readlines()

Écriture

La méthode write :

with open('my_file.csv','w') as file
    content = file.write("Content")

Lecture

La méthode read :

with open('my_file.csv') as f:
    content = f.readlines()

for line in content:
    print(line)

L’ensemble du contenu se trouvant dans le fichier est stocké dans la variable content. La boucle permet de ligne ce content ligne par ligne.

Ce contenu contient \n pour indiquer les retours à la ligne. Pour ne pas avoir cette indication, utilise splitlines() :

content = f.read().splitlines()

Les exceptions

Tu peux générer une erreur parce que le fichier n’existe pas :

file = open('my_file.csv')
IOError: [Errno 2] No such file or directory: 'my_file.csv'

Ou si tu n’as pas les droits :

file = open('my_file.csv')
IOError: [Errno 13] Permission denied: 'my_file.csv'

Ou si tu essaies de lire un dossier :

file = open('my_file.csv')
IOError: [Errno 21] Is a directory

J’en passe et des meilleurs.

Pour éviter ces erreurs il est recommandé d’utiliser la structure try :

try:    
    with open('my_file.csv') as f:
        for l in file:
            print(l)
except:
    print('Something went wrong!')

Si une erreur est levée Python exécute la clause except sinon il l’ignore.

Partie 9 : Notebook

Qu’est-ce qu’un Notebook ?

Sans Notebook, vous écrivez  votre programme dans un fichier puis le lancer :

python my_file.py

Cette méthode est efficace sur de petits scripts à la maison.

En revanche, on perd en efficacité lorsqu’on souhaite lancer une partie isolée du programme. Il arrive par exemple qu’on ait plusieurs graphes pour montrer différentes choses, il n’est pas très intéressant de tout afficher d’un coup et avoir à commenter le code pour afficher une partie est chiant.

Le Notebook permet de répondre à ce problème en isoler le code dans des cellules.

Installation

Installation du Notebook via l’utilitaire pip :

python -m pip install --upgrade pip
python -m pip install jupyter

Puis :

jupyter notebook

Un serveur local s’est lancé.

Créer un nouveau Notebook

Avant toute chose, il en vient il en vient de créer un notebook :

Maintenant il est temps d’entrer du code dans la cellule :

Ctrl+ENTRER pour exécuter une cellule. De cette manière, l’ouput est généré dans la même cellule. On le constate via l’indice :

Shift+ENTRER permet de faire la même chose mais en générant un nouvel input dans lequel saisir du code :

La touche X permet de couper une cellule.

V pour coller.

C pour copier.

Les flèches pour naviguer de cellule en cellule.

La touche A pour ajouter une cellule au dessus. Ou la touche B pour en dessous.

Quand la cellule est en cours de traitement, il y a une petite astérisque :

Presser la touche D deux fois pour supprimer une cellule.

On peut créer des cellules dédiées à la documentation en ajustant le mode markdown :

Ou presser la touche M.

Et la touche Y pour remettre la cellule en mode code.