Python Input/Output¶
Python et les fichiers¶
La fonction open() renvoie un objet de type file et est le plus souvent utilisée avec deux arguments : open(nomfichier, mode).
workfile = open('workfile', 'w')
C’est une bonne pratique d’utiliser le mot-clé with lorsque vous traitez des fichiers. Vous fermez ainsi toujours correctement le fichier, même si une exception est levée. Utiliser with est aussi beaucoup plus court que d’utiliser l’équivalent avec des blocs try-finally:
with open('workfile') as workfile:
read_data = workfile.read() # read all file content as a string
lines = workfile.readlines() # read all lines and store them in a list
with open('writefile', 'w') as writefile:
writefile.write('Hello World')
Avertissement
Si vous n’utilisez pas le mot clef with, vous devez appeler workfile.close() pour fermer le fichier et immédiatement libérer les ressources système qu’il utilise.
Indication
Les modules os et os.path permettent de manipuler facilement des fichiers.
import os
os.remove('workfile') # Delete workfile
os.path.exists('workfile') # Return True if workfile exist else False
os.path.getsize('workfile') # Return file size
os.path.isfile('workfile') # Return True if workfile is a file
Fichier CSV et Python¶
Comma-separated values
Comma-separated values, connu sous le sigle CSV, est un format texte ouvert représentant des données tabulaires sous forme de valeurs séparées par des virgules. Le format fut utilisé pendant des années avant qu’aient lieu des tentatives de standardisation avec la RFC 4180. L’absence de format bien défini signifie que des différences subtiles existent dans la production et la consommation de données par différentes applications. Ces différences peuvent gêner lors du traitement de fichiers CSV depuis des sources multiples.
ref.,name,price,release-date
IEL-51619,Ishtar - Les Jardins De Babylone,40.0,2019-10-04
BLU-KIN01,Kingdomino,19.90,2016-10-21
BO-DRA01,Draftosaurus,19.90,2019-02-22
FLY004JU,Jurassic Snack,19.90,2018-04-06
ref. |
name |
price |
release-date |
|---|---|---|---|
IEL-51619 |
Ishtar - Les Jardins De Babylone |
40.0 |
2019-10-04 |
BLU-KIN01 |
Kingdomino |
19.90 |
2016-10-21 |
BO-DRA01 |
Draftosaurus |
19.90 |
2019-02-22 |
FLY004JU |
Jurassic Snack |
19.90 |
2018-04-06 |
Note
Le délimiteur de colonnes peut être autre chose qu’une virgule, il arrive fréquemment d’utiliser le point-virgule plutôt que la virgule comme délimiteur.
Le module csv fournit des classes pour lire et écrire des données tabulaires au format CSV.
import csv
with open('eggs.csv') as csvfile:
reader = csv.reader(csvfile, delimiter='|')
for row in reader:
print(row)
import csv
with open('eggs.csv', 'w') as csvfile:
writer = csv.writer(csvfile, delimiter='|')
writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
Indication
Les classes DictReader et DictWriter permettent de travailler sur des dictionnaires plutôt que sur des séquences.
Créer un CLI¶
L’utilisation du module argparse comme module « d’analyse d’arguments de ligne de commande » est recommandé et est disponible dans la bibliothèque standard Python.
import argparse
parser = argparse.ArgumentParser(description="This is a usefull description")
parser.add_argument("echo", help="echo the string you use here")
parser.add_argument("square", help="display a square of a given number", type=int)
parser.add_argument("---verbosity", help="increase output verbosity") # optionnal args
args = parser.parse_args()
print(args.echo)
print(args.square)
Le module atexit permet d’enregistrer des appels de fonctions à faire en sortie d’un programme pour faire le « ménage ». Les fonctions sont appelées :
en sortie « normale » de programme, y compris avec un
sys.exit(1)par exemple.si le programme se termine à cause d’une exception.
si le programme se termine à cause d’une erreur python (fonction appelée non existante, ou évaluation de None + 1 par exemple).
si on fait un
ctrl + Cpour interrompre le programme (SIGINT).mais pas d’appel si on tue le programme par
SIGTERM(kill -15) ouSIGKILL(kill -9).
import atexit
def close_connection(connection):
if connection:
connection.close()
atexit.register(close_connection, connection)
Le module sqlite3¶
Le module sqlite3, intégré dans la bibliothèque standard de Python, a été écrit par Gerhard Häring. Il fournit une interface SQL conforme à la spécification DB-API 2.0 décrite par la PEP 249.
Note
DB-API 2.0 est une API définie pour encourager la similarité entre les modules Python utilisés pour accéder aux bases de données.
Nous n’allons pas ici détailler la spécification DB-API 2.0 mais décrire comment utilisé succinctement le module sqlite3.
Une rapide introduction à SQLite¶
l s’agit d’une bibliothèque écrite en langage C. Elle propose un moteur de base de données relationnelles accessible par le langage SQL, et implémente le standard SQL-92 et les propriétés ACID. Ce moteur est développé depuis 2000 D. Richard Hipp. La bibliothèque ainsi que son code source sont sous la licence du domaine public.
- Particularité
Sa particularité est de ne pas reproduire le schéma habituel client-serveur mais d’être directement intégrée aux programmes. L’accès à une base de données avec SQLite se fait par l’ouverture du fichier correspondant à celle-ci. Chaque base de données est enregistrée dans un fichier qui lui est propre, avec ses déclarations, ses tables et ses index mais aussi ses données.
Il n’y a pas d’extension propre aux fichiers de base de données de SQLite, il est courant de rencontrer des extensions comme
.sqliteou.db, parfois suivie du numéro de version de la bibliothèque (.sqlite3,.db2, etc.).- Popularité
Il s’agit d’un des moteurs de bases de données les plus utilisés au monde, celui-ci est notamment utilisé par Firefox, Skype, Google Gears, Android, iOS, des sites web, etc.
Une des raisons de son succès est qu’elle est intégrée dans les bibliothèques standard de nombreux langages comme
Python, Ruby, PHP, C#, C++, etc.De plus son extrême légèreté (moins de 600 Kio), la rende populaire sur les systèmes embarqués.- Portabilité
Étant entièrement écrite en
C-ANSI, la version normalisée du langage de programmationC, celle-ci est compilable sans modification sur toutes les architectures,Ainsi les fichiers de base de données SQLite sont entièrement indépendants du système d’exploitation et de l’architecture.
- Type de données
Il existe plusieurs types d’affinité dans SQLite, définissant la façon dont celui-ci va travailler lors de l’entrée des nouvelles données.
TEXT NUMERIC INTEGER REAL NONE
Note
Une interface en ligne de commande (en anglais command line interface, couramment abrégée CLI) est une interface homme-machine dans laquelle la communication entre l’utilisateur et l’ordinateur s’effectue en mode texte au sein d’un terminal (console).
SQLite embarque une interface en ligne de commande (CLI) simple et pratique pour créer et requêter sur une base de données SQLite. Son utilisation se fait simplement par la commande sqlite3
$ sqlite3 -version
3.26.0 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt1
.exit --Permet de quitter la commande CLI
.table --Liste l'ensemble des tables de la base
.dbinfo --Information sur la base
.dump --Obtenir un dump sql de la base
.schema --Obtenir le code de création de la base
.headers (on, off) --Affichage ou non des entêtes
.mode (list, column, csv, ..) --Affichage en liste, etc.
.version --Information de version
.import --Import de données dans la base
Pour se conncter à une base de données, il suffit d’indiquer le nom du fichier de base de données en argument de la commande sqlite3:
$ sqlite3 boardgames.sqlite3
Attention
Si le fichier existe alors la commande ouvre la base de données sinon celle-ci est créée avant ouverture.
Indication
Voici quelques astuces utiles avec la commande sqlite3 à faire après chaque lancement de la commande:
$ sqlite3 boardgames.sqlite3
sqlite> .headers on -- Display column name
sqlite> .mode columns -- Change display mode, try it
sqlite> .tables -- Show all db tables
sqlite> PRAGMA table_info(nom_table); -- Show structure of nom_table
Il est recommandé d'écrire les requêtes (de création de tables par exemple) dans un fichier :code:`.sql` et de le jouer ensuite via la commande :code:`sqlite3`
.. code-block:: bash
$ sqlite3 boardgames.sqlite3
sqlite> .read creation_schema.sql
Rappel de SQL¶
CREATE TABLE contacts (
id INTEGER PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT,
phone TEXT NOT NULL CHECK (length(phone) >= 10)
);
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
list_price DECIMAL (10, 2) NOT NULL,
discount DECIMAL (10, 2) NOT NULL
DEFAULT 0,
CHECK (list_price >= discount AND
discount >= 0 AND
list_price >= 0)
);
/* Sélection */
SELECT [ * / columns_name ] FROM tablename;
/* Compter */
SELECT COUNT(column_name) FROM table_name;
/* Trier */
SELECT column_name(s) FROM table_name ORDER BY column_name(s) ASC|DESC;
/* INSERTION */
INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...);
/* MAJ */
UPDATE table_name SET column1=value, column2=value2,... WHERE some_column=some_value;
/* SUPPRESSION */
DELETE FROM table_name WHERE some_column=somevalue;
\end{minted}
Utilisation du module sqlite3¶
Pour utiliser le module, vous devez d’abord créer une instance de la classe Connection qui représente la base de données.
Dans cet exemple, les données sont stockées dans le fichier example.sqlite3 :
import sqlite3
connection = sqlite3.connect('example.sqlite3')
Une fois que vous avez une instance de Connection, vous pouvez créer un objet Cursor et appeler sa méthode execute() pour exécuter des commandes SQL :
cursor = connection.cursor()
# Create table
cursor.execute('''CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real)''')
# Insert a row of data
cursor.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
# Save (commit) the changes
connection.commit()
Attention
Par défaut, la connexion n’est pas en autocommit, il faut committer explicitement les ordres SQL via l’appel à la méthode commit.
Avertissement
Vos requêtes SQL utilisent souvent les valeurs de variables. Vous ne devez pas assembler votre requête à l’aide d’opérations sur les chaînes de caractères, car cela n’est pas sûr et rend votre programme vulnérable à une attaque par injection SQL.
# Never do this -- insecure!
symbol = 'RHAT'
cursor.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
À la place, nous allons utiliser la capacité de substitution des paramètres. Placez un ? comme indicateur partout où vous voulez utiliser une valeur, puis fournissez un tuple de valeurs comme second argument de la méthode execute().
values = ('RHAT',)
cursor.execute('SELECT * FROM stocks WHERE symbol=?', values)
# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
('2006-04-06', 'SELL', 'IBM', 500, 53.00),
]
cursor.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)
Pour récupérer des données après avoir exécuté une instruction SELECT, vous pouvez considérer le curseur comme un itérateur, appeler la méthode du curseur fetchone() pour récupérer une seule ligne correspondante ou appeler fetchall() pour obtenir une liste des lignes correspondantes.
cursor.execute('SELECT * FROM stocks WHERE symbol=?', ('RHAT',))
print(cursor.fetchone())
cursor.execute('SELECT * FROM stocks ORDER BY price')
print(cursor.fetchall())
for row in cusror.execute('SELECT * FROM stocks ORDER BY price'):
print(row)
Pour en savoir plus direction la documentation sqlite3
Le module logging¶
La journalisation (logging en anglais) est une façon de suivre les événements qui ont lieu durant le fonctionnement d’un logiciel. Le développeur du logiciel ajoute des appels à l’outil de journalisation dans son code pour indiquer que certains événements ont eu lieu.
Un événement est décrit par un message descriptif, qui peut éventuellement contenir des données variables (c’est-à-dire qui peuvent être différentes pour chaque occurrence de l’événement). Un événement a aussi une importance que le développeur lui attribue ; cette importance peut aussi être appelée niveau ou sévérité.
Quand utiliser logging ?
Le module logging fournit un ensemble de fonctions debug(), info(), warning(), error() et critical(). Pour plus d’information voir la documentation du module logging.
Pour déterminer quand employer quel niveau référez-vous au tableau suivant:
Tâche que vous souhaitez mener |
Le meilleur outil pour cette tâche |
|---|---|
Sortie console d’un script |
|
Rapporter des évènements qui ont lieu au cours du fonctionnement normal d’un programme |
|
Émettre un avertissement (warning en anglais) |
|
Rapporter une erreur sans lever d’exception |
|
Les fonctions de journalisation sont nommées d’après le niveau ou la sévérité des évènements qu’elles suivent. Les niveaux standard et leurs applications sont décrits ci-dessous (par ordre croissant de sévérité) :
Niveau |
Quand il est utilisé |
|---|---|
DEBUG |
Information détaillée, intéressante seulement lorsqu’on diagnostique un problème. |
INFO |
Confirmation que tout fonctionne comme prévu. |
WARNING |
Quelque chose d’inattendu a eu lieu le logiciel fonctionne encore normalement. |
ERROR |
Un problème plus sérieux, le logiciel n’a pas été capable de réaliser une tâche. |
CRITICAL |
Une erreur sérieuse, indiquant que le programme lui-même pourrait être incapable de continuer à fonctionner. |
Note
Le niveau par défaut est WARNING, ce qui signifie que seuls les évènements de ce niveau et au-dessus sont suivis, sauf si le paquet logging est configuré pour faire autrement.
Les évènements suivis peuvent être gérés de différentes façons. La manière la plus simple est de les afficher dans la console. Une autre méthode commune est de les écrire dans un fichier.
import logging
logging.warning('Watch out!') # will print a message to the console
logging.info('I told you so') # will not print anything
Il est très commun d’enregistrer les évènements dans un fichier:
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
Attention
L’appel à basicConfig() doit être fait avant un appel à debug(), info(), etc. Comme l’objectif est d’avoir un outil de configuration simple et d’usage unique, seul le premier appel aura un effet, les appels suivants ne font rien.
Indication
Si votre programme est composé de plusieurs modules, voici une façon d’organiser l’outil de journalisation :
import logging
import mylib
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logging.info('Started')
mylib.do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
import logging
def do_something():
logging.info('Doing something')
Pour changer le format utilisé pour afficher le message, vous devez préciser le format que vous souhaitez employer :
import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.basicConfig(format='%(asctime)s %(message)s')