Scrips et modules Python
Documentation:
- bibliothèque standard
- liste des modules Python et leur documentation
Utilisation de modules
import os
help(os)
help(os.getlogin)
- Quel service rend le module
os
? (cf. documentation)
os.getlogin()
os.getcwd()
os.environ
os.environ['PATH'].split(':')
- Quelle commande shell correspond à
os.environ
? - Quelle structure de données implémente
os.environ
? - Que fait la méthode
split(':')
?
from os import getlogin
getlogin()
- Comment importer toutes les définitions du modules
os
d’un coup?
Premiers scripts
Autant que possible, essayez d’écrire les scripts Python suivants de la manière la plus simple et la plus conforme possible au style Python (définition en compréhension, itérations, utilisation de la bibliothèque standard, etc).
Script moyenne.py
Compléter le script suivant:
notes = [10, 8, 12.5, 18, 8.75]
# calculer la moyenne des notes et la stocker dans une variable `moyenne`
# ...
print(moyenne)
Résultat attendu:
$ python moyenne.py
11.45
Solution 1
notes = [10, 8, 12.5, 18, 8.75]
s = 0
for n in notes:
s += n
moyenne = s / len(notes)
print(moyenne)
Solution 2
notes = [10, 8, 12.5, 18, 8.75]
moyenne = sum(notes) / len(notes)
print(moyenne)
Script syracuse.py
Écrire un script qui, pour une variale n
, fait le calcul suivant:
afficher n
tant que n =/= 1:
si n est pair:
diviser n par 2
sinon
multiplier n par 3 et lui ajouter 1
afficher n
Essayer avec plusieurs valeurs de n
.
Solution 1
n = 28
while n != 1:
if n % 2 == 0:
n /= 2
else:
n = n * 3 + 1
print(n)
Solution 2
n = 28
while n != 1:
n = n * 3 + 1 if n % 2 else n/2
print(n)
Script palindrome.py
Compléter le script ci-dessous. Un palindrome est un mot qui est identique lu de gauche-à-droite ou de droite-à-gauche.
mot = 'kayak'
# tester si c'est un palindrome
# ..
if is_palindrome:
print(mot, 'est un palindrome')
else:
print(mot, "n'est pas un palindrome")
Tester avec différents mots.
Solution 1
mot = 'kayak'
is_palindrome = True
for i in range(len(mot)//2):
if mot[i] != mot[-i-1]:
is_palindrome = False
break
if is_palindrome:
print(mot, 'est un palindrome')
else:
print(mot, "n'est pas un palindrome")
Solution 2
mot = 'kayak'
is_palindrome = mot == mot[::-1]
if is_palindrome:
print(mot, 'est un palindrome')
else:
print(mot, "n'est pas un palindrome")
Script filtre_paire.py
Écrire un script qui filtre une liste de chaînes de caractères en ne gardant que les chaînes dont la longueur est paire.
l1 = ['arbre', 'vers', 'ville', 'vélo']
# construire une liste l2 qui ne contient que les mots de l1 de longueur paire
# ...
print(l2)
Résultat attendu:
$ ./filtre_paire.py
['vers', 'vélo']
Solution 1
l1 = ['arbre', 'vers', 'ville', 'vélo']
l2 = []
for mot in l1:
if len(mot) % 2 == 0:
l2.append(mot)
print(l2)
Solution 2
l1 = ['arbre', 'vers', 'ville', 'vélo']
l2 = [mot for mot in l1 if len(mot) % 2 == 0]
print(l2)
Script triangle.py
Écrire un script triangle.py
qui commence par:
n = 5
# faire une boucle pour afficher le triangle
# *
# **
# ***
# ... (jusqu'à n)
Il doit donner le résultat suivant:
$ python triangle.py
*
**
***
****
*****
Tester pour différentes valeurs de n
.
Solution
n = 5
# faire une boucle pour afficher le triangle
for i in range(1, n+1):
print("*"*i)
Version avancée: modifier l’affichage pour produire:
$ ./triangle.py 5
*
***
*****
*******
*********
Solution
n = 5
# faire une boucle pour afficher le triangle
for i in range(1, n+1):
print(" "*(n-i) + "*"*(i+i-1))
Script organize_strings.py
Étape 1
Étant donné une liste de mots, les ranger dans un dictionnaire avec pour clé la première lettre du mot.
liste = ['barbe', 'arbre', 'ville', 'vers', 'veille']
# construire dictionnaire `index`
index = {}
# ...
print(index)
Résultat attendu (l’ordre des clés et des mots est sans importance):
$ python organize_strings.py
{"a": ["arbre"], "v": ["vers", "ville", "veille"], "b": ["barbe"]}
Solution
liste = ["barbe", "arbre", "ville", "vers", "veille"]
# construire dictionnaire `index`
index = {}
for mot in liste:
key = mot[0]
if key not in index:
index[key] = [mot]
else:
index[key].append(mot)
print(index)
Étape 2
Modifier votre script pour qu’il affiche les clés et les mots dans l’ordre lexicographique:
$ python organize_strings.py
Index a:
- arbre
Index b:
- barbe
Index v:
- veille
- vers
- ville
Solution
liste = ["barbe", "arbre", "ville", "vers", "veille"]
# construire dictionnaire `index`
index = {}
for mot in liste:
key = mot[0]
if key not in index:
index[key] = [mot]
else:
index[key].append(mot)
for key in sorted(index):
print("Index", key)
for mot in sorted(index[key]):
print(" -", mot)
Étape 3
Modifier le script pour que les clés soient la longueur des mots
Solution
liste = ["barbe", "arbre", "ville", "vers", "veille"]
# construire dictionnaire `index` avec clé = longueur du mot
index = {}
for mot in liste:
key = len(mot)
if key not in index:
index[key] = [mot]
else:
index[key].append(mot)
# afficher l'index en triant les clés/mots
for key in sorted(index):
print("Index", key)
for mot in sorted(index[key]):
print(" -", mot)
Étape 4
Modifier le script pour que les clés soient les lettres composant le mot (sans répétition, par ordre alphabétique):
$ python organize_strings.py
Index aber
- arbre
- barbe
Index eilv
- veille
- ville
Index ersv
- vers
Solution
liste = ["barbe", "arbre", "ville", "vers", "veille"]
# construire dictionnaire `index` avec clé = lettres du mots triées, sans répétition
index = {}
for mot in liste:
key = "".join(sorted(set(mot)))
if key not in index:
index[key] = [mot]
else:
index[key].append(mot)
# afficher l'index en triant les clés/mots
for key in sorted(index):
print("Index", key)
for mot in sorted(index[key]):
print(" -", mot)
Arguments de ligne de commande
Les arguments de ligne de commande
Créer un fichier test_args.py
avec le contenu suivant:
import sys
print(sys.argv)
Appeler le script avec différents arguments:
$ ./test_args.py
$ ./test_args.py a 2 b
$ ./test_args.py --size 3
Définition d’une interface de ligne de commande
Le module argparse
(cf. documentation) permet de définir facilement les options de ligne de commande attendues par un script.
Par exemple, le script ci-dessous permet de calculer la factorielle de n
:
n
étant lu sur la ligne de commande- en choisissant un algorithme de calcul avec l’option
--algorithme
Par exemple: $ ./fact.py 5 --algorithme recursif
calcule 5!
en utilisant un algorithme récursif.
#!/usr/bin/env python
import argparse
import sys
def factorial_rec(n):
"""Retourne la factorielle de n, avec n >= 0, calculée par un algorithme récursif."""
return 1 if n == 0 else n * factorial_rec(n - 1)
def factorial_iter(n):
"""Retourne la factorielle de n, avec n >= 0, calculée par un algorithme itératif."""
f = 1
for i in range(1,n+1):
f *= i
return f
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('n', nargs=1, help=""" Entier n, avec n >= 0""")
parser.add_argument('--algorithme', type=str, default='recursif', help=""" Algorithme: iteratif ou recursif (par défaut: recursif)""")
args = parser.parse_args()
n = int(args.n[0])
if args.algorithme == 'recursif':
print(f'{n}! est égale à {factorial_rec(n)}')
elif args.algorithme == 'iteratif':
print(f'{n}! est égale à {factorial_iter(n)}')
else:
print("choix d'algorithme invalide")
Que fait la commande ./fact.py -h
? Essayer d’exécuter le script ci-dessus avec différentes entrées.
Étape 1
Modifier le script moyenne.py
afin qu’il lise la liste des nombres depuis la ligne de commande
Solution
#!/usr/bin/env python
import argparse
def moyenne(l):
return sum(l) / len(l)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('n', nargs='+', help=""" liste de nombres dont on calcule la moyenne """)
args = parser.parse_args()
numbers = [int(x) for x in args.n]
m = moyenne(numbers)
print(m)
Étape 2 (avancé)
Modifier le script organize_strings.py
pour qu’il lise la liste de mots sur la ligne de commande, et qu’il choisisse le type de clé grace à l’option --cle
qui peut valoir: premiere_lettre
, longueur
, lettres
.
Solution
#!/usr/bin/env python
import argparse
import sys
def cle_premiere_lettre(mot):
"""Retourne la premiète lettre de mot comme clé"""
return mot[0]
def cle_longueur(mot):
"""Retourne la longueur de mot comme clé"""
return len(mot)
def cle_lettres(mot):
"""Retourne l'ensemble trié des lettres de mot comme clé"""
return "".join(sorted(set(mot)))
def construit_index(liste, cle):
"""Retourne le dictionnaire des mots de liste en utilisant cle pour déterminer la clé de chaque mot"""
index = {}
for mot in liste:
key = cle(mot)
if key not in index:
index[key] = [mot]
else:
index[key].append(mot)
return index
def affiche_index(index):
"""Affiche index par ordre croissant de mots et de clés"""
for key in sorted(index):
print("Index", key)
for mot in sorted(index[key]):
print(" -", mot)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--cle', action='store', help='type de clé', default='premiere_lettre')
parser.add_argument('mots', nargs='+', help='liste de mots à indexer')
args = parser.parse_args()
cles = {'premiere_lettre': cle_premiere_lettre,
'longueur': cle_longueur,
'lettres': cle_lettres}
if args.cle not in cles:
print('Choix de clé invalide')
sys.exit()
index = construit_index(args.mots, cles[args.cle])
affiche_index(index)
Manipulation de fichiers
Rappels sur les fichiers
La fonction open
permet d’ouvrir un fichier en mode lecture, écriture ou ajout:
f = open('file.txt') # mode lecture seul
f = open('file.txt', mode='w') # mode écriture, efface le contenu du fichier
f = open('file.txt', mode='a') # mode écrtiure, depuis la fin du fichier
L’objet retourné dispose de méthodes pour lire (read
, readline
, readlines
), écrire (write
, writelines
), et le fermer (close
).
Exemples:
f = open('data.txt') # mode lecture par défaut
content = f.read() # lit la totalité
f.close()
with open('data.txt') as f:
content = f.readlines() # liste avec un élément par ligne (fin de ligne inclus)
# le fichier est automatiquement fermé à la fin du bloc
out = open('log.txt', mode='a') # mode ajout
out.write('Hello world!\n')
# on peut aussi utiliser `print`:
print("Bonjour le monde!", file=out)
out.close()
Le module sys
donne accès à la sortie standard (sys.stdout
), la sortie d’erreur (sys.stderr
) et l’entrée standard (sys.stdin
) présentés comme des fichiers (mêmes méthodes).
Script moyennes.py
Soit le fichier notes.txt
avec le contenu suivant:
Bob,8
Alice,19
Charlie,14
Bob,14
Charlie,20
Alice,13
Bob,16
Alice,18
Charlie,9
Étape 1
Écrire un script moyennes.py
qui prend en argument de ligne de commande le nom du fichier, qui charge les notes, puis affiche pour chaque nom la moyenne de ses notes.
Solution
#!/usr/bin/env python
import argparse
def moyenne(l):
"""Calcule la moyenne d'une liste de nombres."""
return sum(l) / len(l)
def lire_notes(f):
"""Retourne un dictionnaire qui associe aux noms les listes de notes contenues dans le fichier f"""
notes = {}
for ligne in f.readlines():
nom, note = ligne.strip().split(',')
note = int(note)
if nom not in notes:
notes[nom] = [note]
else:
notes[nom].append(note)
return notes
def afficher_moyennes(notes):
"""Affiche les moyennes des notes, où notes est un dictionnaire associant à chaque nom sa liste de notes"""
for nom in notes:
print(f'{nom}: {moyenne(notes[nom])}')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('filename', nargs=1, help=""" nom du fichier de notes """)
args = parser.parse_args()
with open(args.filename[0]) as f:
notes = lire_notes(f)
afficher_moyennes(notes)
Étape 2
Modifier votre script pour qu’il lise sur son entrée standard si aucun nom de fichier est fourni en ligne de commande. Testez votre script en utilisant le pipe du shell: cat data.txt | ./moyennes.py
.
Solution
#!/usr/bin/env python
import argparse
import sys
def moyenne(l):
"""Calcule la moyenne d'une liste de nombres."""
return sum(l) / len(l)
def lire_notes(f):
"""Retourne un dictionnaire qui associe aux noms les listes de notes contenues dans le fichier f"""
notes = {}
for ligne in f.readlines():
nom, note = ligne.strip().split(',')
note = int(note)
if nom not in notes:
notes[nom] = [note]
else:
notes[nom].append(note)
return notes
def afficher_moyennes(notes):
"""Affiche les moyennes des notes, où notes est un dictionnaire associant à chaque nom sa liste de notes"""
for nom in notes:
print(f'{nom}: {moyenne(notes[nom])}')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('filename', nargs='?', help=""" nom du fichier de notes """)
args = parser.parse_args()
f = sys.stdin if not args.filename else open(args.filename)
notes = lire_notes(f)
afficher_moyennes(notes)
if f != sys.stdin:
f.close()
Interfaçage C-Python (avancé)
Cette partie concerne la comparaison des temps de calcul en langages C et Python, ainsi que la mise en oeuvre en langage C, d’un module Python. L’exercice s’appuie sur le calcul de la suite de Fibonacci:
\[\begin{align*} F_0 & = 1\\ F_1 & = 1\\ F_n & = F_{n-1} + F_{n-2} \text{ si } n\ge 2\\ \end{align*}\]- Écrire un script Python
fibonacci.py
qui prend un entiern
en ligne de commande et affiche $F_n$. - Écrire un programme C
fibonacci.c
qui prend également un entiern
en ligne de commande et affiche $F_n$. - Comparer les temps d’exécution des deux programmes pour des valeurs croissantes de
n
(débuter avecn=10
, puis des valeurs de5
en5
). - En suivant ce tutoriel, programmer:
- dans le fichier
fibmodule.c
, la fonction C définie dans le fichierfibonacci.c
, ainsi que les fonctions et structures de données nécessaires à la définition du modulefib
- dans le fichier
setup.py
, les définitions nécessaires à la compilation du module avecdistutils
.
- dans le fichier
- Importer le module
fib
dans l’interface REPLpython
, et vérifier son bon fonctionnement. Comparer les temps de calcul du modulefib
avec votre programme C et votre script python.