Sunday, March 22, 2015

Deux modules utiles

Deux modules utiles

D’abord, qu’est-ce qu’un module ? Il s’agit d’une sorte de bibliothèque (un regroupement de fonctions prédéfinies) qui une fois importée permet d’accéder à de nouvelles fonctions. Il en existe beaucoup. On peut citer :
  • le module turtle qui permet de réaliser des dessins géométriques,
  • le module numpy qui permet de faire du calcul scientifique,
  • le module sympy qui permet de faire du calcul formel,
  • le module matplotlib qui permet de faire des graphiques en tout genre. Pour ceux que cela interesse, il existe ce petit tutoriel pour débuter avec matplotlib .
Il y en a beaucoup d’autres, tant dans la nature (https://github.com/search?q=python+module) que dans la bibliothèque standard (http://docs.python.org/3/py-modindex.html), voire des modules que vous aurez codés vous-mêmes. Parmi ces derniers, j’ai choisi de présenter les deux qui, en classe de seconde au moins, me semblent les plus utiles: math et random.

Le module math

C’est un module qui permet d’avoir accès aux fonctions mathématiques comme le cosinus (cos), le sinus (sin), la racine carrée (sqrt), le nombre \pi (pi) et bien d’autres...
>>> from math import *  # importation du module
>>> cos(pi)             # cosinus d'un angle en radian
-1.0
>>> sqrt(25)            # racine carrée
5.0
En plus de ce qui a été cité précédemment, on peut ajouter les fonctions tangente (tan), partie entière (floor), exponentielle (exp) et logarithme népérien (log).
Concernant la fonction partie entière, il y en avait déjà une sans l’apport du module math. Il s’agit de int. Vous noterez une légère différence entre les deux.
>>> floor(12.123)
12.0
>>> int(12.123)
12

Le module random

En anglais “random” signifie “le hasard”. Ce module va nous permettre d’utiliser des fonctions générant des nombres aléatoires. Les deux qui me semblent le plus utile dans un premier temps sont : - random() qui renvoie un nombre aléatoire entre 0 et 1, - choice(liste) qui choisit au hasard un nombre dans une liste donnée.
>>> from random import *
>>> random()
0.34461947461259612
>>> random()
0.024762749258158245
>>> L = [7, 3, 8, 5, 6]
>>> choice(L)
5
>>> choice(L)
3
>>> # cela marche pour une liste de chaine de caractères
>>> liste = ['Pierre', 'Paul', 'Jacques']
>>> choice(liste)
'Jacques'
Pour aller plus loin
Comme dit plus haut, il y a différents types de modules :
  • ceux qui sont inclus dans la version de Python comme random ou math,
  • ceux que l’on peut rajouter comme numpy ou matplotlib,
  • et ceux que l’on peut faire soi-même (il s’agit dans les cas simples d’un fichier Python contenant un ensemble de fonctions).
Mais pour importer un module, la façon de procéder reste la même, ou plutôt les mêmes car il y a deux façons de faire. Voyons cela en prenant comme exemple le module math.
>>> from math import sqrt
>>> sqrt(36)
6.0
>>> cos(0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'cos' is not defined
Le message d’erreur signifie simplement que l’on n’a pas importé la fonction cos. Donc si on recommence
>>> from math import sqrt, cos
>>> sqrt(36)
6.0
>>> cos(0)
1.0
Et si on veut que toutes les fonctions du module soient importées, on peut procéder comme on l’a fait plus haut en utilisant l’étoile *.
>>> from math import *
>>> sqrt(36)
6.0
>>> cos(0)
1.0
C’était la première façon de faire. Voici la deuxième façon de faire pour importer un module.
>>> import math
>>> math.sqrt(36)
6.0
>>> math.cos(0)
1.0
Bien que cette deuxième méthode paraisse plus lourde à écrire, elle peut avoir certains avantages.

Listes en compréhension

Listes en compréhension

C’est une idée reconnue mais peu de langages de programmation l’ont adoptée: la possibilité de créer des listes de manière à la fois concise et élégante.
Profitons-en !
La syntaxe est très proche de la manière dont, en maths, on peut décrire certains ensembles:
\{ f(x) ; x \in A \} et \{ x \in A \ | P(x) \}
Notons tout d’abord que pour les mathématiciens, un ensemble n’est pas ordonné, alors que pour Python, une liste est ordonnée et peut contenir plusieurs fois le même élément.

Au plus simple: copie de la liste

Dans sa version la plus simple, la syntaxe est [x for x in ma_liste], où x est une variable muette et ma_liste est la liste à parcourir.
[x for x in ma_liste] peut se lire «les x quand x parcourt la liste ma_liste».
>>> liste = [1, 2, 3]
>>> [x for x in liste]
[1, 2, 3]
On obtient une copie de la liste, ce qui n’est à vrai dire pas très intéressant. De plus on pouvait le faire encore plus facilement avec liste[:]. Passons à quelque chose de plus sérieux.

Avec filtrage par un test

Nous avons vu au chapitre Tests (instruction if) que l’on pouvait exécuter un bloc d’instructions ou un autre suivant le résultat d’un test, comme un aiguillage.
Ici, si la condition suivant le if est vraie, l’élément correspondant sera inclus dans la liste finale, et sera exclu sinon. Notez que nous n’avons pas besoin de else.
[x for x in ma_liste if condition_sur_x] peut se lire «la liste des x quand x parcourt la liste ma_liste mais seulement les x pour lesquels la condition est vraie».
>>> liste = [1, 2, 3]
>>> [x for x in liste if x > 1]
[2, 3]
Les tests peuvent aussi être donnés sous la forme de Fonctions. En effet, si test est une fonction qui retourne un booléen, alors on peut l’utiliser pour filtrer avec la syntaxe [x for x in ma_liste if test(x)].
[x for x in ma_liste if test(x)] peut se lire «la liste des x quand x parcourt la liste ma_liste, tels que test(x) est vrai».
>>> def pair(nbre):
...     return nbre % 2 == 0
...
>>> [x for x in range(0, 10) if pair(x)]
[0, 2, 4, 6, 8]

Avec application d’une fonction

Une fois qu’une fonction est définie (numérique ou non, voir Fonctions), elle peut être appliquée au nombres de la liste de départ, les images constitueront la liste d’arrivée.
[f(x) for x in ma_liste] peut se lire «la liste des f(x) quand x parcourt la liste ma_liste».
On obtient le même résultat en mettant directement une expression en x à la place de f(x).
[2*x for x in ma_liste] peut se lire «la liste des doubles de x quand x parcourt la liste ma_liste».
>>> [2*x for x in range(0, 10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Filtrage et application d’une fonction

Les deux derniers paragraphes peuvent se combiner. [f(x) for x in ma_liste if test(x)] peut se lire «la liste des f(x) quand x parcourt la liste ma_liste, tels que test(x) est vrai».

Boucles (instructions while et for)

Boucles (instructions while et for)

Tant que

Le principe de la boucle while, c’est d’exécuter un bloc d’instructions tant que (while in english) une condition donnée est vraie. Encore une fois commençons par un exemple :
>>> i = 1
>>> while i <= 5:
...     print(i)
...     i = i + 1
...
1
2
3
4
5
Ici tant que (while) la condition (i\leq5) est vraie, le bloc d’instructions (il y en a deux dans notre cas) est executé.

Attention
La deuxième instruction i = i + 1 est primordiale car elle assure que l’on va sortir de la boucle. En effet, ce qu’il faut éviter avec les boucles tant_que, c’est de construire une boucle sans fin. C’est ce qui peut se produire si la condition du while n’est jamais vérifiée.
Pour savoir combien de fois la boucle while est executée, il peut être utile d’utiliser une variable qui servira de compteur, c’est-à-dire une variable (qu’il ne faut pas oublier d’initialiser) et incrémentée de 1 dans le bloc d’instructions.

Pour

Lorsque l’on souhaite répéter un nombre donné de fois la même instruction ou le même bloc d’instructions, la commande forest la plus appropriée.
Admettons que l’on veuille afficher 5 fois le mot blabla. Voici ce que l’on peut faire.
>>> for i in range(5):  # pour i allant de 0 à 4
...     print("blabla")
...
blabla
blabla
blabla
blabla
blabla
Bon, arrêtons là le blabla et regardons un exemple plus mathématique. Si on veut afficher les carrés des entiers de 1 à 7 :
>>> for i in range(1, 8):   # pour i allant de 1 à 7
...     print(i**2)
...
1
4
9
16
25
36
49
Puis terminons sur un exemple classique qui est le calcul de la somme des premiers entiers. Disons ici que l’on s’arrête à 30. Autrement dit, on veut calculer
1+2+3+\dots+30
que l’on note aussi
\sum_{i=1}^{30}\, i
>>> S = 0
>>> for i in range(1, 31):   # pour i allant de 1 à 30
...     S = S + i
... print(S)
...
465

Tests (instruction if)

Tests (instruction if)

Premier contact

Commençons par regarder le script ci-dessous :
print("Entrer un nombre plus petit que 100.")
nb = float(input())

if nb < 100:
    print("Ce nombre convient.")
else:
    print("Ce nombre est trop grand.")
A l’exécution du script, l’ordinateur va afficher la chaîne “Entrer un nombre plus petit que 100.” et va marquer un temps d’arrêt. En effet, la commande input() attend ici que l’on rentre un nombre et que l’on appuie sur la touche Entrée. Mais ce nombre est d’abord saisi en tant que chaine de caractères, puis converti en nombre (flottant) grâce à la fonction float. Ce nombre sera alors affecté à la variable nb.
Puis le test commence :
  • si (IF en anglais) la condition (nb < 100) est vraie, alors le bloc d’instructions qui suit (ici une seule instruction) est executé,
  • sinon (ELSE en anglais), c’est-à-dire si la condition (nb < 100) est fausse, alors c’est l’autre bloc d’instructions qui est executé.
Note
Ici les deux blocs d’instructions ne sont composés que d’une seule instruction.
Le couple if et else agit finalement comme un aiguillage. Attention cependant, l’instruction else et son bloc sont parfois omis. Dans ce cas, si la condition est fausse, rien n’est exécuté.
Attention
Avec Python3, la fonction input() renvoie systématiquement une chaîne de caractères. Donc si c’est un nombre que l’on veut manipuler, il faudra utiliser la fonction int() s’il s’agit d’un entier ou float() s’il s’agit d’un décimal. Par exemple :
chaine = input()
nb = float(chaine)
ou bien directement
nb = float(input())

Comparaison de nombres

Vous aurez souvent à comparer des nombres. Voici une liste des opérateurs que vous pourrez utiliser.
  • x == y est vrai quand x est égal à y,
  • x != y est vrai quand x est différent de y,
  • x > y est vrai quand x est strictement supérieur à y,
  • x < y est vrai quand x est strictement inférieur à y,
  • x >= y est vrai quand x est supérieur ou égal à y, et
  • x <= y est vrai quand x est inférieur ou égal à y.

Doubles inégalités

Python permet de plus l’enchaînement des comparaisons.
En mathématiques, il n’est pas rare que l’on ait à écrire 3 \leq x \leq 7 pour dire que x appartient à l’intervalle [3;7]. En Python, cela peut être fait grâce au connecteur and, comme dans les autres langages.
>>> for x in range(5):
...     if 3 <= 2*x and 2*x <= 7:  # long et peu lisible
...         print(x, "est dans l'intervalle")
...     else:
...         print(x, "n'est pas dans l'intervalle")
...
(0, "n'est pas dans l'intervalle")
(1, "n'est pas dans l'intervalle")
(2, "est dans l'intervalle")
(3, "est dans l'intervalle")
(4, "n'est pas dans l'intervalle")
Mais c’est bien plus lisible avec la double inégalité, et chaque expression (ici 2x) n’est évaluée qu’une fois.
>>> for x in range(5):
...     if 3 <= 2*x <= 7:  # plus concis et plus lisible
...         print(x, "est dans l'intervalle")
...     else:
...         print(x, "n'est pas dans l'intervalle")
...
(0, "n'est pas dans l'intervalle")
(1, "n'est pas dans l'intervalle")
(2, "est dans l'intervalle")
(3, "est dans l'intervalle")
(4, "n'est pas dans l'intervalle")

Compléments sur les booléens

En début de chapitre nous avons vu que certaines conditions pouvaient être «vraies» ou «fausses». Une branche des mathématiques appellée «logique» s’occupe justement de ce genre de problèmes et les booléens en sont les briques élémentaires. En Python, les mots True et False représentent respectivement «vrai» et «faux» (comme en anglais en fait).
>>> True
True
>>> False
False
>>> true  # attention à la majuscule !!!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'true' is not defined
>>> 0 == 0
True
>>> 0 > 1
False
Pour aller plus loin
La négation s’exprime avec not.
>>> not True
False
>>> not False
True
>>> not 0 == 0
False
On peut combiner deux booléens entre eux avec les opérateurs and et or. Ce sont respectivement les opérateurs logiques «et» et «ou» (comme en anglais aussi). Ci-dessous nous demandons à Python d’écrire les tables de vérité de ces deux opérateurs.
>>> for a in [False, True]:
...     for b in [False, True]:
...         print(a, "and", b, "vaut", a and b)
...
False and False vaut False
False and True vaut False
True and False vaut False
True and True vaut True
>>> for a in [False, True]:
...     for b in [False, True]:
...         print(a, "or", b, "vaut", a or b)
...
False or False vaut False
False or True vaut True
True or False vaut True
True or True vaut True
Vous trouverez d’autres informations dans la documentation de Python au sujet des booléens et des opérateurs logiques.

Fonctions

Fonctions

Les fonctions comme on les connaît 

On peut définir une fonction d’une variable. Considérons par exemple la fonction affine f suivante définie sur \mathbb{R} par:
f : x\longmapsto 2x+1
Pour la définir en Python :
>>> def f(x):
...     return 2 * x + 1
...
>>> # on peut maintenant l'utiliser
>>> f(0)
1
>>> # et la réutiliser
>>> f(5)
11
Attention
Vous avez remarqué qu’à la deuxième ligne, on n’a pas commencé à écrire au début de la ligne. On dit qu’on fait une indentation. Et cette indentation est indispensable pour que Python fasse son travail. En règle générale, le bloc d’instructions (une ou plusieurs lignes) qui dépend d’une ligne (devant elle se terminer par :) doit être indenté. C’est obligatoire et en plus cela a l’avantage de rendre le script plus lisible. Vous avez dû remarquer qu’à cette occasion, le prompt ... remplace le >>> .
Le principe de définition de fonctions est intéressant pour au moins deux raisons:
  • cela nous permet de ne pas répéter un calcul long à taper,
  • car Python possède un type spécial dédié au fonctions, que l’on peut donc manipuler, mettre dans des listes pour les étudier les unes à la suite des autres...
>>> f
<function f at 0xb71839ec>

Les fonctions comme base de la programmation

Les fonctions vues en seconde prennent souvent un nombre pour en calculer l’image. Mais en programmation, on rencontre aussi beaucoup de fonctions prenant du texte, une liste, une fonction ou ne prenant pas de paramètre du tout. Elles peuvent aussi retourner du texte, une liste, une fonction, voire même ne rien retourner.
La fonction suivante n’a pas de paramètre et ne retourne rien non plus (pas de return).
>>> def hi():
...     print("Hello from Python!!!")
...
>>> hi()
Hello from Python!!!
La fonction ne renvoie rien mais ne chôme pas pour autant, elle écrit quelque chose à l’écran. On peut, grâce à ce genre de fonction, lister une suite de tâches répétitives que l’on pourra lancer d’un seul coup. C’est pratique aussi pour clarifier le programme, car on peut en séparer les différentes parties.
La fonction suivante prend du texte en paramètre et ne renvoie toujours rien.
>>> def hi(name):
...     print("hello " + name + " from Python!!!")
...
>>> hi("John")
hello John from Python!!!
Le travail est ainsi fait “en fonction” du texte passé en paramètre.
La fonction suivante ne prend pas de paramètre mais va retourner une lettre au hasard.
>>> from random import choice
>>> def lettre():
...     return choice('abcdefghijklmnopqrstuvwxyz')
...
>>> lettre()
'm'
>>> lettre()
'r'
Pour enfoncer le clou sur la différence entre une fonction qui retourne quelque chose et une autre qui ne retourne rien, examinons cet exemple:
>>> def f(x): return 2 * x
...
>>> def g(x): print(2 * x)
...
>>> f(3)
6
>>> g(3)
6
>>> # MAIS
>>> f(3) == 6
True
>>> g(3) == 6
6
False
Beaucoup de combinaisons sont possibles pour les fonctions, à vous de construire les bonnes suivant vos besoins, en fonction de la façon dont vous pensez découper votre programme.