Table des matières

publicité
Table des matières
Préface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
1. Texte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Traiter une chaîne caractère par caractère
7
1.2 Convertir des caractères en leurs codes numériques et réciproquement 8
1.3 Tester si un objet est assimilé à une chaîne
1.4 Aligner des chaînes
1.5 Supprimer les espaces aux extrémités d’une chaîne
1.6 Combiner des chaînes
1.7 Inverser une chaîne par mots ou par caractères
1.8 Tester si une chaîne contient certains caractères
1.9 Simplifier l’utilisation de la méthode translate des chaînes
1.10 Filtrer une chaîne par rapport à un ensemble de caractères
1.11 Tester si une chaîne contient du texte ou du binaire
1.12 Contrôler la casse
1.13 Accéder à des sous-chaînes
1.14 Modifier l’indentation d’une chaîne de plusieurs lignes
1.15 Convertir les tabulations
1.16 Interpréter des variables dans une chaîne
1.17 Interpréter des variables dans des chaînes avec Python 2.4
1.18 Remplacer plusieurs motifs en une seule passe
1.19 Tester si une chaîne a plusieurs fins possibles
1.20 Gérer du texte international avec Unicode
1.21 Convertir des chaînes Unicode en chaînes normales
et réciproquement
1.22 Afficher des caractères Unicode sur la sortie standard
1.23 Encoder des données Unicode pour XML et HTML
9
11
11
12
15
16
19
22
25
26
28
31
32
35
36
38
41
43
45
47
49
vi
Table des matières
1.24 Rendre des chaînes insensibles à la casse
1.25 Convertir des documents HTML en texte sur un terminal Unix
51
55
2. Fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.1 Lire un fichier
2.2 Écrire dans un fichier
2.3 Rechercher et remplacer du texte dans un fichier
2.4 Lire une ligne spécifique dans un fichier
2.5 Compter les lignes d’un fichier
2.6 Traiter tous les mots d’un fichier
2.7 Utiliser des entrées/sorties à accès direct
2.8 Modifier un fichier à accès direct
2.9 Lire des données contenues dans un fichier zip
2.10 Gérer un fichier zip dans une chaîne
2.11 Archiver une arborescence de fichiers
dans une archive tar compressée
2.12 Envoyer des données binaires sur la sortie standard de Windows
63
67
68
69
70
73
75
76
78
79
2.15 Adapter un objet apparenté à un fichier à un véritable objet fichier
2.16 Parcourir des arborescences de répertoires
81
82
83
85
88
89
2.17 Remplacer une extension de fichier par une autre dans toute une
arborescence de répertoires
2.18 Trouver un fichier dans un chemin donné
90
92
2.13 Utiliser une syntaxe comme celle de iostream en C++
2.14 Revenir au début d’un fichier d’entrée
2.19 Chercher dans un chemin les fichiers font les noms correspondent
à un certain motif
2.20 Rechercher un fichier dans le chemin de recherche de Python
2.21 Modifier dynamiquement le chemin de recherche de Python
2.22 Calculer le chemin relatif d’un répertoire par rapport à un autre
2.23 Lire un caractère non tamponné de façon portable
2.24 Compter les pages d’un document PDF sur Mac OS X
2.25 Modifier les attributs de fichiers sur Windows
2.26 Extraire du texte d’un document OpenOffice.org
2.27 Extraire le texte d’un document Word de Microsoft
2.28 Verrouiller un fichier avec une API portable
2.29 Ajouter des numéros de versions à des noms de fichiers
2.30 Calculer une somme de contrôle CRC-64
93
94
95
96
98
100
101
102
103
104
106
108
3. Temps et argent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.1 Calculer les dates d’hier et de demain
117
Table des matières
3.2 Trouver le vendredi précédent
3.3 Calculer l’écart séparant deux dates
3.4 Totaliser des durées de chansons
3.5 Calculer le nombre de jours de travail entre deux dates
3.6 Rechercher automatiquement les jours fériés
3.7 Analyser des dates de manière floue
3.8 Vérifier si l’heure d’été est active
3.9 Convertir des fuseaux horaires
3.10 Lancer plusieurs fois une commande
3.11 Planifier des commandes
3.12 Utiliser l’arithmétique décimale
3.13 Formater des décimaux comme des valeurs monétaires
3.14 Utiliser Python comme une simple calculatrice
3.15 Vérifier la somme de contrôle d’une carte bancaire
3.16 Surveiller les taux de changes monétaires
vii
119
120
121
123
125
128
129
130
132
133
135
137
140
143
144
4. Raccourcis Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
4.1 Copier un objet
4.2 Construire des listes avec des listes en intension
4.3 Renvoyer un élément d’une liste s’il existe
4.4 Boucler sur les éléments et les indices d’une séquence
4.5 Créer des listes de listes sans partager les références
4.6 Aplatir une séquence imbriquée
4.7 Supprimer ou réordonner des colonnes dans une liste de lignes
4.8 Transposer des tableaux à deux dimensions
4.9 Obtenir une valeur dans un dictionnaire
4.10 Ajouter une entrée à un dictionnaire
4.11 Construire un dictionnaire sans mettre les clés entre apostrophes
4.12 Construire un dictionnaire à partir d’une liste de clés
et de valeurs alternées
4.13 Extraire un sous-ensemble d’un dictionnaire
4.14 Inverser un dictionnaire
4.15 Associer plusieurs valeurs à une clé de dictionnaire
4.16 Utiliser un dictionnaire pour lancer des méthodes ou des fonctions
4.17 Trouver l’union et l’intersection de deux dictionnaires
4.18 Rassembler plusieurs éléments nommés
4.19 Affecter et tester en une seule instruction
4.20 Utiliser printf avec Python
149
152
154
155
156
158
160
162
164
165
167
168
170
172
173
175
176
179
181
183
viii
Table des matières
4.21 Choisir aléatoirement des éléments avec des probabilités données
4.22 Gérer les exceptions dans une expression
4.23 Vérifier qu’un nom est défini dans un module donné
184
185
187
5. Programmation orientée objet . . . . . . . . . . . . . . . . . 191
5.1 Convertir des températures
5.5 Déléguer automatiquement plutôt qu’hériter
198
200
202
204
206
5.6 Déléguer des méthodes spéciales dans les mandataires
209
5.7 Implémenter des tuples avec des éléments nommés
212
5.8 Éviter les accesseurs passe-partout pour les propriétés
214
5.9 Faire une copie rapide d’un objet
216
5.2 Définir des constantes
5.3 Restreindre l’ajout d’attributs
5.4 Chaîner des recherches dans des dictionnaires
5.10 Conserver des références vers des méthodes liées sans empêcher
le travail du ramasse-miettes
218
5.11 Implémenter un tampon circulaire
221
5.12 Tester les changements d’état d’une instance
224
5.13 Vérifier qu’un objet a les attributs nécessaires
228
5.14 Implémenter le motif de conception « État »
231
5.15 Implémenter le motif de conception « Singleton »
233
5.16 Éviter le motif de conception Singleton grâce à l’idiome Borg
235
5.17 Implémenter le motif de conception Objet nul
239
5.18 Initialiser automatiquement les variables d’instances à partir des
paramètres de __init__
242
5.19 Appeler la méthode __init__ d’une super-classe si elle existe
244
5.20 Utiliser les appels coopératifs aux super-classes
de façon concise et sûre
247
6. Persistance et bases de données . . . . . . . . . . . . . . . . 251
6.1 Sérialiser des données avec le module marshal
254
6.2 Sérialiser des données avec les modules pickle et cPickle
256
6.3 Utiliser la compression avec la sérialisation
259
6.4 Utiliser le module cPickle avec des classes et des instances
260
6.5 Sérialiser des objets contenant des méthodes liées
263
6.6 Sérialiser des objets code
265
6.7 Modifier des objets avec shelve
267
6.8 Utiliser des fichiers Berkeley DB
270
Table des matières
6.9 Accéder à une base de données MySQL
6.10 Stocker un BLOB dans une base de données MySQL
6.11 Stocker un BLOB dans une base de données PostgreSQL
6.12 Stocker un BLOB dans une base de données SQLite
6.13 Créer un dictionnaire associant les noms de colonnes
à leurs numéros
6.14 Utiliser dtuple pour accéder simplement aux résultats des requêtes
6.15 Afficher proprement le contenu des curseurs de bases de données
6.16 Utiliser le même style de passage de paramètres
pour tous les modules de l’API DB
6.17 Utiliser Microsoft Jet via ADO
6.18 Accéder à une base de données JDBC à partir d’une servlet Jython
6.19 Utiliser ODBC pour récupérer des données Excel à partir de Jython
ix
273
274
275
277
279
281
283
285
287
289
292
7. Débogage et tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
7.1 Empêcher l’exécution de certaines instructions conditionnelles
et de certaines boucles
7.2 Mesurer l’utilisation de la mémoire avec Linux
7.3 Déboguer le processus de ramasse-miettes
7.4 Capturer et enregistrer les exceptions
7.5 Tracer les expressions et les commentaires pendant le débogage
7.6 Obtenir plus d’informations de la pile des appels
7.7 Lancer automatiquement le débogueur après une exception non
capturée
7.8 Lancer des tests unitaires le plus simplement possible
7.9 Lancer automatiquement des test unitaires
7.10 Utiliser doctest avec unittest et Python 2.4
296
297
299
300
302
305
308
309
311
312
7.11 Vérifier que des valeurs appartiennent à des intervalles dans les tests
315
unitaires
8. Traitement de XML . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
8.1 Vérifier qu’un document XML est bien formé
8.2 Compter les balises d’un document
8.3 Extraire le texte d’un document XML
8.4 Détecter automatiquement l’encodage d’un document XML
8.5 Convertir un document XML en arborescence d’objets Python
8.6 Supprimer les nœuds textuels ne contenant que des espaces
dans le sous-arbre DOM d’un nœud
8.7 Analyser un document XML produit par Microsoft Excel
319
320
321
323
325
327
328
x
Table des matières
8.8 Valider des documents XML
8.9 Filtrer les éléments et les attributs qui appartiennent
à un certain espace de noms
8.10 Fusionner des événements textes contigus avec un filtre SAX
8.11 Utiliser MSHTML pour traiter un document XML ou HTML
330
331
333
336
9. Programmation réseau . . . . . . . . . . . . . . . . . . . . . . . . 339
9.1 Envoyer des messages en utilisant des datagrammes
9.2 Récupérer un document sur le Web
9.3 Filtrer une liste de sites FTP
9.4 Obtenir l’heure à partir d’un serveur SNTP
9.5 Envoyer du mail en HTML
9.6 Intégrer des fichiers dans un message MIME
9.7 Décomposer un message MIME multi-volet
9.8 Supprimer les pièces jointes d’un courrier électronique
9.9 Corriger les messages traités par le module email.FeedParser
de Python 2.4
9.10 Consulter une boîte POP3 de façon interactive
9.11 Détecter les ordinateurs inactifs
9.12 Surveiller un réseau avec HTTP
9.13 Faire suivre et rediriger des ports
9.14 Créer un tunnel SSL à travers un proxy
9.15 Implémenter le protocole Dynamic IP
9.16 Se connecter à IRC et enregistrer les messages sur disque
9.17 Accéder à des serveurs LDAP
341
343
344
345
346
348
351
352
355
357
359
364
367
369
372
376
378
10. Programmation web . . . . . . . . . . . . . . . . . . . . . . . . . . 381
10.1 Vérifier que CGI fonctionne
10.2 Gérer des URL dans un script CGI
10.3 Déposer des fichiers sur un serveur web avec CGI
10.4 Vérifier qu’une page web existe
10.5 Tester le type de contenu avec HTTP
10.6 Reprendre le téléchargement d’un fichier par HTTP
10.7 Traiter les cookies pendant la récupération des pages web
10.8 S’authentifier sur un proxy pour naviguer en HTTPS
10.9 Exécuter une servlet avec Jython
10.10 Trouver un cookie d’Internet Explorer
10.11 Produire des fichiers OPML
382
385
386
388
389
391
392
395
396
398
400
Table des matières
xi
10.12 Agréger des flux RSS
403
10.13 Transformer des données en pages web grâce aux « templates »
406
10.14 Formater des objets avec Nevow
409
11. Itérateurs et générateurs . . . . . . . . . . . . . . . . . . . . . . . 413
11.1 Écrire une fonction comme range, mais avec des valeurs flottantes
416
11.2 Construire une liste à partir d’un itérable quelconque
418
11.3 Générer la suite de Fibonacci
420
11.4 N’obtenir que quelques éléments d’une séquence
avec une affectation multiple
422
11.5 Obtenir automatiquement le nombre voulu d’éléments
d’une séquence
423
11.6 Diviser un itérable en n tranches de pas n
425
11.7 Boucler sur une séquence avec des fenêtres glissantes
427
11.8 Parcourir en parallèle plusieurs itérables
431
11.9 Parcourir le produit cartésien de plusieurs itérables
433
11.10 Lire un fichier texte paragraphe par paragraphe
436
11.11 Lire des lignes avec des caractères de continuation
438
11.12 Parcourir un flux de blocs de données comme un flux de lignes
440
11.13 Utiliser un générateur pour récupérer de gros ensembles résultats
à partir d’une base de données
441
11.14 Fusionner des séquences triées
443
11.15 Effectuer des permutations, des combinaisons et des sélections
447
11.16 Produire les partitions d’un entier
449
11.17 Dupliquer un itérateur
451
11.18 Rechercher vers l’avant dans un itérateur
454
11.19 Simplifier l’écriture des threads consommateurs
456
11.20 Exécuter un itérateur dans un autre thread
457
11.21 Établir un rapport récapitulatif avec itertools.groupby
459
12. Descripteurs, décorateurs et métaclasses . . . . . . . . 463
12.1 Obtenir de nouvelles valeurs par défaut à chaque appel de fonction
465
12.2 Coder des propriétés comme des fonctions imbriquées
468
12.3 Créer des alias pour les valeurs des attributs
470
12.4 Mettre des valeurs d’attributs en cache
472
12.5 Utiliser une seule méthode pour accéder à plusieurs attributs
475
12.6 Ajouter une fonctionnalité à une classe en enveloppant
une méthode
476
xii
Table des matières
12.7 Ajouter une fonctionnalité à une classe en enrichissant toutes ses
méthodes
12.8 Ajouter en cours d’exécution une méthode à une instance de classe
12.9 Vérifier que des classes implémentent des interfaces
12.10 Utiliser correctement __new__ et __init__ avec les métaclasses
personnalisées
12.11 Permettre l’enchaînement de méthodes de modification de listes
12.12 Utiliser des appels coopératifs à super avec une syntaxe plus courte
12.13 Initialiser des attributs d’instance sans utiliser __init__
12.14 Initialiser automatiquement les attributs d’instance
12.15 Mettre automatiquement à jour des instances de classes
lorsqu’elles sont rechargées
12.16 Lier des constantes à la compilation
12.17 Résoudre les conflits de métaclasses
479
481
484
485
487
489
491
493
496
500
505
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
CHAPITRE 2
Fichiers
Introduction
Par Mark Lutz, auteur de Programming Python et Python Quick Reference, et co-auteur
de Learning Python.
Tout programmeur raisonnablement pragmatique exigera d’un langage de programmation des outils performants pour traiter les fichiers. Le traitement des fichiers
externes étant une tâche très réelle, la qualité des interfaces permettant de les manipuler est effectivement un bon moyen d’évaluer l’aspect pratique d’un outil de programmation.
Comme le prouvent les recettes de ce chapitre, Python excelle dans cette tâche
puisqu’il permet de gérer les fichiers à différents niveaux : de la fonction prédéfinie
open (un synonyme du type objet file standard) aux outils tiers disponibles sur le
Web, en passant par les outils spécialisés des modules de la bibliothèque standard,
comme os. En d’autres termes, l’arsenal de Python pour les fichiers offre des outils
puissants dont vos scripts pourront tirer parti.
Introduction aux fichiers
Un objet fichier Python est une instance du type prédéfini file. La fonction
prédéfinie open crée et renvoie un objet fichier. Son premier paramètre, une chaîne,
indique le chemin du fichier (c’est-à-dire son nom précédé éventuellement de son
chemin d’accès). Le second paramètre, qui est également une chaîne, précise son
mode d’ouverture. Voici un exemple :
entree = open('donnees', 'r')
sortie = open('/tmp/spam', 'w')
Quelle que soit la syntaxe utilisée par le système d’exploitation sous-jacent, le chemin
d’accès passé en paramètre à open est formé de noms de répertoires et d’un nom de
fichier, séparés par des caractères « barre de fraction » (/). Sur les systèmes qui n’utilisent pas les barres de fraction, vous pouvez utiliser à la place le caractère anti-slash
(\), mais il n’y a pas de raison réelle de le faire : les anti-slash sont plus difficiles à
placer dans les littéraux chaînes car vous devez les doubler ou utiliser des chaînes
« brutes ». Si le chemin d’accès passé en paramètre ne contient pas le nom du réper-
60
Chapitre 2 — Fichiers
toire du fichier, celui-ci est supposé se trouver dans le répertoire courant (qu’il ne faut
pas confondre avec le chemin de recherche sys.path des modules Python).
Pour le paramètre du mode, 'r' signifie que l’on souhaite lire le fichier en mode
texte ; c’est la valeur par défaut et elle est fréquemment omise de sorte que open n’est
alors appelée qu’avec un seul paramètre. Les autres modes courants sont 'rb' pour
lire le fichier en mode binaire, 'w' pour créer et écrire dans le fichier en mode texte
et 'wb' pour créer et écrire dans le fichier en mode binaire. 'rU' est une variante
parfois précieuse de 'r' : elle demande à Python de lire le fichier en mode texte en
utilisant des « caractères de nouvelle ligne universels » : ce mode permet de lire des
fichiers texte indépendamment de la convention de terminaison des lignes utilisée,
que ce soit celle d’Unix, de Windows ou même des anciens Mac (le Mac OS X actuel
est un Unix sous tous rapports, mais les versions de Mac OS 9 et antérieures, qui
remontent seulement à quelques années, étaient très différentes).
Sur les plates-formes non Unix, la distinction entre mode texte et mode binaire est
importante à cause des caractères de fin de ligne utilisés par ces systèmes. Lorsque
vous ouvrez un fichier en mode binaire, Python sait qu’il n’a pas à se soucier de ces
caractères : il se contente de déplacer les octets entre le fichier et les chaînes en
mémoire sans effectuer aucun traitement. Lorsque vous ouvrez un fichier en mode
texte sur des systèmes non Unix, par contre, Python sait qu’il doit effectuer une traduction entre les caractères de fin de ligne '\n' utilisés dans les chaînes et les caractères de fin de ligne qu’utilise la plate-forme courante dans les fichiers. Tant que vous
indiquez correctement le mode binaire ou texte lorsque vous ouvrez un fichier, votre
code Python peut toujours considérer que '\n' est le caractère de fin de ligne.
Une fois l’objet fichier obtenu, toutes les opérations d’entrées-sorties sur le fichier
s’effectuent en appelant les méthodes de cet objet. Lorsque vous avez terminé de travailler avec le fichier, vous devriez terminer en appelant la méthode close de l’objet,
qui ferme la connexion avec le fichier :
entree.close()
Dans les petits scripts, les programmeurs omettent souvent cette étape car Python
ferme automatiquement le fichier lorsqu’un objet fichier est nettoyé par le ramassemiettes (ce qui, en Python classique, signifie que le fichier n’est fermé qu’une seule
fois, bien que d’autres implémentations importantes du langage, comme Jython et
IronPython, aient des stratégies de ramasse-miettes moins rigides). Quoi qu’il en soit,
fermer vos fichiers le plus tôt possible est une bonne habitude de programmation,
notamment conseillée dans les programmes plus gros qui, sinon, prennent le risque
d’avoir un nombre excessif de fichiers ouverts inutilement. L’idiome try/finally est
particulièrement bien adapté pour garantir qu’un fichier sera correctement fermé,
même si une fonction se termine à cause d’une exception non capturée.
Pour écrire dans un fichier, utilisez la méthode write :
sortie.write(chaine)
où chaine est une chaîne que vous pouvez considérer comme une chaîne de caractères si sortie est ouvert en mode d’écriture texte ou d’octets s’il est ouvert en mode
d’écriture binaire. Les fichiers disposent d’autres méthodes liées à l’écriture, comme
flush qui écrit toutes les données encore dans le tampon et writelines qui écrit une
séquence de chaînes d’un seul coup. Cependant, write est, de loin, la méthode d’écriture la plus utilisée.
Introduction
61
La lecture d’un fichier est une opération plus courante que l’écriture, qui provoque
plus de problèmes. C’est la raison pour laquelle les objets fichiers possèdent plus de
méthodes de lecture que de méthodes d’écriture. La méthode readline lit et renvoie
la ligne suivante d’un fichier texte. La boucle suivante :
while True:
ligne = entree.readline()
if not ligne: break
traiter(ligne)
était, à une époque, l’idiome classique en Python, mais ce n’est plus le meilleur
moyen de lire et de traiter toutes les lignes d’un fichier. Une autre possibilité
ancienne consiste à utiliser la méthode readlines, qui lit tout le fichier et renvoie une
liste de lignes :
for ligne in entree.readlines():
traiter(ligne)
readlines n’est utile que pour les fichiers qui peuvent tenir confortablement dans la
mémoire physique. Si le fichier est vraiment énorme, readlines peut échouer ou, au
moins, ralentir considérablement le traitement (la mémoire virtuelle se remplit et le
système d’exploitation doit commencer à écrire des parties de la mémoire physique
sur le disque). Avec les versions actuelles de Python, il suffit de boucler sur l’objet
fichier lui-même pour obtenir une ligne à la fois avec d’excellentes performances, tant
au niveau de l’exécution qu’au niveau de la mémoire :
for ligne in entree:
traiter(ligne)
Bien sûr, on ne souhaite pas toujours lire un fichier ligne par ligne ; vous pouvez
vouloir lire quelques octets du fichiers, voire tous, notamment si vous l’avez ouvert
en mode de lecture binaire, où le concept de lignes ne peut bien sûr pas s’appliquer.
En ce cas, vous pouvez utiliser la méthode read qui, lorsqu’elle est appelée sans
paramètre, lit et renvoie tous les octets restants du fichier. Avec un paramètre entier
N, read lit et renvoie les N octets suivants (ou tous ceux qui restent s’il y en a moins
que N). Les autres méthodes méritant d’être mentionnées sont seek et tell, qui permettent d’effectuer des accès directs dans les fichiers ; elles sont généralement utilisées
avec des fichiers binaires formés d’enregistrements de longueur fixe.
Portabilité et souplesse
Vue de l’extérieur, la gestion des fichiers par Python est intuitive. Cependant, avant
d’utiliser les codes de ce chapitre, je voudrais souligner deux aspects de la gestion des
fichiers par Python : la portabilité du code et la souplesse des interfaces.
N’oubliez pas que la plupart des interfaces de Python pour les fichiers sont entièrement portables entre les différentes plates-formes. Il serait difficile d’exagérer l’importance de cette fonctionnalité. Un script Python qui parcourt tous les fichiers d’une
arborescence de répertoires à la recherche d’un texte, par exemple, peut sans problème passer d’une plate-forme à une autre sans devoir modifier son code source : il
suffit de copier le fichier source du script sur la nouvelle machine. Je le fais d’ailleurs
constamment — si souvent que je peux avec bonheur rester à l’écart de la guerre des
systèmes d’exploitation. Grâce à la portabilité de Python, la plate-forme sous-jacente
n’a quasiment pas d’importance.
62
Chapitre 2 — Fichiers
En outre, le fait que les interfaces de traitement des fichiers en Python ne soit pas
restreintes aux fichiers réels, physiques, m’a toujours frappé. En réalité, la plupart des
outils pouvant servir aux fichiers fonctionnent avec n’importe quel type d’objet présentant la même interface qu’un véritable objet fichier. Ainsi, le lecteur d’un fichier
ne se soucie que des méthodes de lecture et celui qui écrit ne s’occupe que des méthodes d’écriture. Tant que l’objet cible implémente le protocole attendu, tout va bien.
La fonction qui suit applique la fonction qui lui est passée en paramètre à chaque
ligne d’un fichier en entrée :
def scanner(objet_fichier, gestionnaire_ligne):
for ligne in objet_fichier:
gestionnaire_ligne(ligne)
Si vous codez cette fonction dans un fichier module et que vous placez ce fichier dans
un répertoire du chemin de recherche de Python (sys.path), vous pourrez l’utiliser
à chaque fois que vous avez besoin d’analyser un fichier ligne par ligne. À titre
d’exemple, voici un script client qui se contente d’afficher le premier mot de chaque
ligne :
from outils import scanner
def premierMot(ligne):
print ligne.split()[0]
fichier = open('donnees')
scanner(fichier, premierMot)
Pour l’instant, nous avons simplement codé un petit composant logiciel réutilisable,
mais vous remarquerez qu’il n’y a aucune déclaration de type dans la fonction
scanner : uniquement une contrainte d’interface — n’importe quel objet itérable
ligne par ligne conviendra. Si, par exemple, vous voulez fournir un test de saisie pour
un objet chaîne au lieu d’un vrai fichier physique, vous pouvez utiliser le module
StringIO ou son équivalent plus rapide cStringIO, qui fournissent tout le nécessaire
pour envelopper les chaînes et créer les interfaces :
from cStringIO import StringIO
from outils import scanner
def premierMot(ligne): print ligne.split()[0]
chaine = StringIO('un\ndeux xxx\ntrois\n')
scanner(chaine, premierMot)
Les objets StringIO étant compatibles avec l’interface des objets fichiers, scanner
prend ses trois lignes de texte à partir d’un objet chaîne en mémoire au lieu de les
lire dans un vrai fichier externe. Vous n’avez pas besoin de modifier le code de la
fonction pour que cela marche : il suffit de lui passer le bon type d’objet. Pour être
plus général encore, vous pouvez même utiliser une classe pour implémenter
l’interface attendue :
class MonFlux(object):
def __iter__(self):
# Recupere et renvoie du texte
return iter(['a\n', 'b c d\n'])
from outils import scanner
def premierMot(ligne):
print ligne.split()[0]
objet = MonFlux()
scanner(objet, premierMot)
Lire un fichier
63
Cette fois-ci, lorsque scanner tente de lire le fichier, il appelle en fait la méthode
__iter__ que vous avez codée dans votre classe. En pratique, une telle méthode peut
utiliser d’autres outils standard de Python pour récupérer du texte à partir d’une
variété de sources : un utilisateur interactif, une boîte de saisie d’une interface graphique, un objet shelve, une base de données SQL, un document XML ou HTML,
une socket réseau, etc. Le point important est que scanner ne connaît pas et ne
s’occupe pas de savoir quel type d’objet implémente l’interface qu’il attend, ni ce que
fait vraiment cette interface.
Les programmeurs utilisant l’approche orientée objet connaissent cette naïveté délibérée sous le terme de polymorphisme. Le type de l’objet traité détermine ce qu’une
opération, comme la boucle for de scanner, fait vraiment. En Python, ce sont les
interfaces des objets, plutôt que les types de données spécifiques, qui constituent les
unités de couplage. L’effet pratique est que les fonctions peuvent souvent s’appliquer
à un domaine de problèmes bien plus large que celui auquel vous pouviez vous
attendre (c’est particulièrement vrai si vous connaissez déjà des langages utilisant un
typage statique comme C ou C++). Le code a une souplesse innée provenant du
typage fort mais dynamique de Python.
En fait, la portabilité et la souplesse du code sont des caractéristiques omniprésentes
dans le développement en Python : elles ne sont pas confinées aux interfaces des
fichiers. Ce sont des fonctionnalités du langage qui bénéficient simplement aux scripts
de traitement des fichiers. D’autres avantages de Python, comme sa facilité d’écriture
et la lisibilité de son code, sont également des points clés lorsque le moment est venu
de modifier des programmes. Mais plutôt que de chanter les louanges de Python ici,
je préfère déléguer aux merveilleuses recettes de ce chapitre et de ce livre la découverte de tous ces détails. Amusez-vous bien !
2.1 Lire un fichier
Par Luther Blissett
Problème
Vous voulez lire du texte ou des données à partir d’un fichier.
Solution
Voici le moyen le plus pratique de lire le contenu entier d’un fichier dans une seule
grande chaîne en une seule opération :
# tout le texte d'un fichier texte
tout_le_texte = open('unfichier.txt').read()
# toutes les donnees d'un fichier binaire
toutes_les_donnees = open('unfichierbinaire', 'rb').read()
Cependant, il est plus sûr de lier l’objet fichier à un nom, afin de pouvoir appeler
close sur cet objet dès que vous en aurez terminé avec lui : cela évite de garder des
fichiers ouverts inutilement. Voici un exemple pour un fichier texte :
objet_fichier = open('unfichier.txt')
try:
tout_le_texte = objet_fichier.read()
64
Chapitre 2 — Fichiers
finally:
objet_fichier.close()
L’instruction try/finally n’est pas nécessaire ici, mais il est conseillé de l’utiliser car
elle garantit que le fichier sera fermé, même si une erreur survient pendant sa lecture.
Le moyen le plus simple, le plus rapide et le plus pythonique de lire en une seule
opération le contenu d’un fichier dans une liste de chaînes, à raison d’une par ligne,
est le suivant :
liste_de_toutes_les_lignes = objet_fichier.readlines()
Cette instruction laisse le '\n' à la fin de chaque ligne ; si vous ne voulez pas d’un
tel comportement, vous avez plusieurs autres possibilités :
liste_de_toutes_les_lignes = objet_fichier.read().splitlines()
liste_de_toutes_les_lignes = objet_fichier.read().split('\n')
liste_de_toutes_les_lignes = [L.rstrip('\n') for L in objet_fichier]
Le moyen le plus simple et le plus rapide de traiter ligne par ligne un fichier texte
consiste simplement à boucler sur l’objet fichier avec une instruction for :
for ligne in objet_fichier:
## Traiter ligne
Cette approche laisse également un '\n' à la fin de chaque ligne ; vous pouvez l’ôter
en commençant le corps de la boucle for par :
ligne = ligne.rstrip('\n')
Si cela ne vous gêne pas de supprimer les espaces terminaux de chaque ligne (pas
seulement le '\n' final), vous pouvez même utiliser la forme générale suivante, qui
est plus pratique :
ligne = ligne.rstrip()
Discussion
À moins que le fichier que vous vouliez lire soit vraiment énorme, le placer entièrement en mémoire en une seule opération est souvent le moyen le plus rapide et le
plus commode pour les traitements ultérieurs. La fonction prédéfinie open crée un
objet fichier Python (vous pouvez aussi appeler le type prédéfini file). Vous appelez
ensuite la méthode read de cet objet pour obtenir tout son contenu (que ce soit du
texte ou du binaire) sous la forme d’une seule longue chaîne. Si le contenu est du
texte, vous pouvez choisir de découper immédiatement cette chaîne en une liste de
lignes grâce à la méthode split ou à l’aide de la méthode spécialisée splitlines. Le
découpage en lignes étant souvent nécessaire, vous pouvez également appeler directement readlines sur l’objet fichier pour effectuer plus rapidement et plus commodément cette opération.
Vous pouvez également boucler directement sur l’objet fichier ou le passer à des
appelables qui attendent un objet itérable, comme list ou max — lorsqu’un objet
fichier ouvert en lecture est traité comme un itérable, ses éléments sont ses lignes de
texte (cela ne doit donc être fait qu’avec des fichiers texte). Ce type d’itération ligne
par ligne est économique en termes de consommation mémoire, tout en restant assez
rapide.
Sur les systèmes Unix et apparentés, comme Linux, Mac OS X et les autres variantes
BSD, il n’existe pas de réelle distinction entre les fichiers texte et les fichiers de don-
Lire un fichier
65
nées binaires. Sur Windows et les très anciens systèmes Macintosh, cependant, les fins
de lignes dans les fichiers texte sont encodées, non pas avec le séparateur standard
'\n' mais, respectivement, avec '\r\n' et '\r'. Python traduit ces caractères de fins
de lignes en '\n' à notre place. Ceci signifie que, lorsque vous ouvrez un fichier
binaire, vous devez l’indiquer à Python afin qu’il n’effectue pas cette traduction : il
suffit, pour cela, d’utiliser 'rb' comme second paramètre de open. Ce paramètre est
inoffensif sur les plates-formes Unix, mais il est conseillé de distinguer les fichiers
binaires des fichiers texte, bien que ce ne soit pas obligatoire dans ce cas-là. Ces bonnes habitudes rendront vos programmes plus facilement compréhensibles et plus
compatibles avec les différentes plates-formes.
Si vous ne connaissez pas la convention utilisée par un fichier texte pour ses fins de
lignes, utilisez 'rU' comme second paramètre de open afin de demander une traduction universelle des fins de lignes. Cela vous permet d’échanger librement et sans
souci des fichiers texte entre Windows, Unix (dont Mac OS X) et les anciens systèmes
Macintosh : tous les types de conventions pour les fins de lignes seront converties en
'\n', quelle que soit la plate-forme sur laquelle s’exécute votre code.
Comme le montre le premier extrait de code de la solution, vous pouvez appeler des
méthodes comme read directement sur l’objet fichier produit par la fonction open
mais, dans ce cas, vous n’aurez plus de référence à cet objet lorsque l’opération de
lecture se sera terminée. En pratique, Python note instantanément l’absence de référence sur l’objet et ferme immédiatement le fichier, mais il est préférable de lier un
nom au résultat de open afin de pouvoir appeler explicitement close lorsque vous
avez terminé de travailler avec le fichier. De la sorte, il restera ouvert le moins longtemps possible, même sur des plates-formes comme Jython, IronPython et d’éventuelles versions futures de Python, sur lesquelles des mécanismes de ramasse-miettes
plus élaborés peuvent retarder la fermeture automatique que la version actuelle de
Python, reposant sur C, effectue immédiatement. Pour garantir qu’un objet fichier est
fermé même si des erreurs surviennent pendant son traitement, l’approche la plus
robuste et la plus prudente consiste à utiliser l’instruction try/finally :
objet_fichier = open('unfichier.txt')
try:
for ligne in objet_fichier:
## Traiter la ligne
finally:
objet_fichier.close()
Faites attention à ne pas placer l’appel à open dans la clause try de cette instruction
try/finally (c’est une erreur assez classique chez les débutants). En effet, si une
erreur survient pendant l’ouverture, il n’y aura rien à fermer et, en outre, rien ne sera
lié au nom objet_fichier : vous ne devriez donc vraiment pas appeler objet_
fichier.close() !
Si vous choisissez de lire le fichier par morceaux au lieu de le lire entièrement d’un
seul coup, la technique sera différente. Voici comment lire un fichier binaire par
morceaux de 100 octets jusqu’à atteindre la fin :
66
Chapitre 2 — Fichiers
objet_fichier = open('unfichierbinaire', 'rb')
try:
while True:
morceau = objet_fichier.read(100)
if not morceau:
break
traiter(morceau)
finally:
objet_fichier.close()
Lorsque vous passez un paramètre N à la méthode read, cela vous garantit que cette
méthode ne lira que les N octets suivants (ou moins, s’il reste moins de N octets à lire
dans le fichier). read renvoie la chaîne vide lorsqu’il atteint la fin du fichier. Les boucles compliquées gagnent à être encapsulées sous la forme de générateurs réutilisables.
Ici, nous ne pouvons effectuer qu’une encapsulation partielle car on ne peut pas utiliser le mot-clé yield d’un générateur dans la clause try d’une instruction try/
finally. Comme try/finally nous garantit que le fichier sera fermé, nous pouvons
donc nous contenter de ce code :
def lire_fichier_par_morceaux(nom_fichier, taille_morceau = 100):
objet_fichier = open(nom_fichier, 'rb')
while True:
morceau = objet_fichier.read(taille_morceau)
if not morceau:
break
yield morceau
objet_fichier.close()
Maintenant que ce générateur lire_fichier_par_morceaux est disponible, le code de
notre application pour lire et traiter un fichier binaire par morceaux de taille fixe
devient extrêmement simple :
for morceau in lire_fichier_par_morceaux('unfichierbinaire'):
traiter(morceau)
Lire un fichier texte ligne par ligne est une opération fréquente. Il suffit pour cela de
boucler sur l’objet fichier, comme dans cet exemple :
for ligne in open('unfichier.txt', 'rU'):
traiter(ligne)
Ici aussi, pour être sûr et certain qu’aucun fichier ne restera ouvert inutilement, vous
pouvez écrire cet exemple de façon plus rigoureuse et plus prudente :
objet_fichier = open('unfichier.txt', 'rU'):
try:
for ligne in objet_fichier:
traiter(ligne)
finally:
objet_fichier.close()
Voir aussi
La recette 2.2 ; la documentation sur la fonction prédéfinie open et sur les objets
fichiers dans la Library Reference et dans Python en concentré.
Écrire dans un fichier
67
2.2 Écrire dans un fichier
Par Luther Blissett
Problème
Vous voulez écrire du texte ou des données dans un fichier.
Solution
Voici le moyen le plus commode pour écrire une seule longue chaîne dans un fichier :
# texte dans un fichier texte
open('unfichier.txt', 'w').write(tout_le_texte)
# donnees dans un fichier binaire
open('unfichierbinaire', 'wb').write(toutes_les_donnees)
Cependant, il est plus sûr de lier l’objet fichier à un nom afin de pouvoir appeler
close sur cet objet dès que vous n’en aurez plus besoin. Voici un exemple pour un
fichier texte :
objet_fichier = open('unfichier.txt', 'w')
objet_fichier.write(tout_le_texte)
objet_fichier.close()
Souvent, les données que vous souhaitez écrire ne se trouvent pas dans une seule
grosse chaîne mais dans une liste (ou toute autre séquence) de chaînes. En ce cas, vous
devriez utiliser la méthode writelines qui, malgré son nom, n’est pas limitée aux
lignes mais fonctionne aussi bien avec des données binaires qu’avec des fichiers texte :
objet_fichier.writelines(liste_de_chaines_de_texte)
open('unfichierbinaire', 'wb').writelines(liste_de_chaines_de_donnees)
L’utilisation de writelines est bien plus rapide que les autres solutions consistant à
joindre les chaînes pour former une seule grande chaîne (avec ''.join, par exemple)
pour appeler ensuite write, ou consistant à appeler write de façon répétée dans une
boucle.
Discussion
Pour créer un objet fichier afin d’y écrire, vous devez toujours passer un deuxième
paramètre à open (ou à file) :'w' pour écrire des données textuelles, 'wb' pour écrire
des données binaires. Les différentes questions évoquées précédemment dans la
recette 2.1 s’appliquent également ici, sauf qu’un appel explicite à close lorsque l’on
écrit dans un fichier est encore plus recommandé que lorsqu’on le lit. En effet, ce n’est
qu’en fermant le fichier que vous pouvez être à peu près sûr que les données sont
écrites sur le disque et ne sont pas encore dans un tampon temporaire en mémoire.
L’écriture morceau par morceau dans un fichier est une opération encore plus fréquente que la lecture par morceaux. Vous pouvez vous contenter d’appeler de façon
répétée write et/ou writelines au fur et à mesure que chaque chaîne ou séquence
de chaînes à écrire est prête. Chaque opération d’écriture ajoute les données à la fin
du fichier, après toutes les données qui y ont déjà été écrites. Lorsque vous avez terminé de travailler avec le fichier, appelez la méthode close sur l’objet fichier. Si toutes les données sont disponibles immédiatement, un seul appel à writelines est plus
68
Chapitre 2 — Fichiers
rapide et plus simple. Par contre, si elles ne sont disponibles que par morceaux à la
fois, il est préférable d’appeler write à mesure qu’elles arrivent plutôt que de construire une liste temporaire des morceaux (avec append par exemple), uniquement
pour pouvoir les écrire en une seule fois à la fin avec writelines. Vous remarquerez
donc qu’il y a une certaine différence entre la lecture et l’écriture en ce qui concerne
les performances et la facilité de manipulation des données.
Lorsque vous ouvrez un fichier en écriture avec le paramètre
'w' (ou 'wb'), toutes les données qui pourraient déjà se trouver
dans le fichier sont immédiatement supprimées ; même si vous
fermez le fichier aussitôt après l’avoir ouvert, vous vous retrouverez avec un fichier vide sur le disque. Si vous voulez ajouter
des données après le contenu existant, ouvrez plutôt le fichier
avec le paramètre 'a' (ou 'ab'). Il existe également des options
d’ouverture plus avancées qui permettent à la fois de lire et
d’écrire dans le même objet fichier — la recette 2.8 présentera
notamment l’option 'r+b' qui, en pratique, est la seule des
options avancées à être couramment utilisée.
Voir aussi
Les recettes 2.1 et 2.8 ; la documentation sur la fonction prédéfinie open et les objets
fichiers dans la Library Reference et dans Python en concentré.
2.3 Rechercher et remplacer du texte dans un fichier
Par Jeff Bauer et Adam Krieg
Problème
Vous devez remplacer une chaîne par une autre dans un fichier.
Solution
Pour substituer des chaînes, le plus simple consiste à utiliser la méthode replace des
objets chaînes. L’exemple qui suit effectue la substitution à partir du fichier indiqué
(ou de l’entrée standard) et écrit le résultat dans le fichier indiqué (ou sur la sortie
standard) :
#!/usr/bin/env python
import os, sys
nb_params = len(sys.argv)
if not 3 <= nb_params <= 5:
print "usage: %s ancien_texte nouveau_texte [fichier_src [fichier_dest]]" % \
os.path.basename(sys.argv[0])
else:
ancien_texte = sys.argv[1]
nouveau_texte = sys.argv[2]
fichier_src = sys.stdin
fichier_dest = sys.stdout
if nb_params > 3:
fichier_src = open(sys.argv[3])
Lire une ligne spécifique dans un fichier
69
if nb_params > 4:
fichier_dest = open(sys.argv[4], 'w')
for s in fichier_src:
fichier_dest.write(s.replace(ancien_texte, nouveau_texte))
fichier_dest.close()
fichier_src.close()
Discussion
Ce qui fait la beauté de cette recette, c’est sa simplicité — pourquoi faire compliqué
lorsque l’on peut faire simple ? Comme l’indique sa première ligne, qui est une ligne
de « shebang », cette recette est conçue pour être lancée directement à partir de la
ligne de commande du shell, c’est-à-dire comme un script ; elle n’a pas été écrite
comme un module destiné à être importé dans un autre programme. Ce script examine les paramètres qui lui ont été passés pour en extraire le texte à rechercher, le
texte de remplacement, le fichier d’entrée (qui est, par défaut, l’entrée standard) et le
fichier de sortie (qui est, par défaut, la sortie standard). Puis, il boucle sur chaque
ligne du fichier d’entrée, en écrivant chacune d’elles dans le fichier de sortie après
avoir effectué la substitution, c’est tout ! Pour être le plus propre possible, le script
ferme les deux fichiers avant de se terminer.
Si le fichier d’entrée peut confortablement tenir en deux exemplaires dans la
mémoire (un avant, l’autre après le remplacement puisque les chaînes ne sont pas
modifiables), nous pourrions accélérer la vitesse d’exécution en traitant d’un seul
coup tout le contenu du fichier au lieu de boucler sur ses lignes. Cela ne devrait pas
poser de problème car le moindre PC actuel est doté d’au moins 256 Mo de mémoire,
ce qui permet de gérer des fichiers pouvant atteindre environ 100 Mo : peu de fichiers
texte dépassent cette taille. Pour cela, il suffit de remplacer la boucle for par une
seule instruction :
fichier_dest.write(fichier_src.read().replace(ancien_texte, nouveau_texte))
Comme vous pouvez le constater, c’est encore plus simple qu’avec la boucle de la
recette.
Voir aussi
La documentation sur la fonction prédéfinie open, sur les objets fichiers et sur la
méthode replace des chaînes dans la Library Reference et dans Python en concentré.
2.4 Lire une ligne spécifique dans un fichier
Par Luther Blissett
Problème
Vous souhaitez lire une ligne précise, dont vous connaissez le numéro, dans un fichier
texte.
70
Chapitre 2 — Fichiers
Solution
Le module linecache de la bibliothèque standard rend cette opération très simple :
import linecache
ligne = linecache.getline(chemin_fichier, num_ligne)
Discussion
Le module standard linecache constitue généralement la solution optimale pour ce
genre d’opération. Il est particulièrement utile si vous devez effectuer plusieurs fois
cette tâche pour plusieurs lignes d’un fichier car il met les informations en cache afin
d’éviter de répéter un travail inutile. Si vous savez que vous n’aurez plus besoin
d’autres lignes du fichier pendant un moment, appelez la fonction clearcache de ce
module afin de libérer la mémoire occupée par le cache. Vous pouvez également utiliser checkcache si le fichier risque d’avoir changé sur le disque et que vous voulez
vous assurer que vous lisez la dernière version de celui-ci.
linecache lit et met en cache le contenu complet du fichier texte dont vous avez
passé le nom en paramètre : si ce fichier est très gros et que vous n’avez besoin que
d’une seule ligne, linecache peut donc faire plus de travail que nécessaire. Si cela
pose des problèmes de performances à votre programme, vous pouvez accélérer le
traitement en écrivant une boucle explicite encapsulée dans une fonction :
def getline(chemin_fichier, num_ligne):
if num_ligne < 1: return ''
for num_ligne_courante, ligne in enumerate(open(chemin_fichier, 'rU')):
if num_ligne_courante == num_ligne - 1: return ligne
return ''
Le -1 dans la comparaison == est nécessaire car enumerate compte à partir de 0 et
nous supposons que le paramètre num_ligne débute à 1.
Voir aussi
La documentation du module linecache dans la Library Reference et dans Python en
concentré ; la recette 8.8 de Perl en action.
2.5 Compter les lignes d’un fichier
Par Luther Blissett
Problème
Vous voulez compter les lignes d’un fichier.
Solution
L’approche la plus simple pour les fichiers de taille raisonnable consiste à lire le
fichier comme une liste de lignes : le nombre de lignes sera donc la longueur de cette
liste. Si le chemin du fichier est lié à la variable chemin_fichier, le seul code nécessaire pour implémenter cette solution est le suivant :
nbre_lignes = len(open(chemin_fichier, 'rU').readlines())
Compter les lignes d’un fichier
71
Si le fichier est vraiment énorme, cette approche peut être très lente, voire échouer.
En ce cas, une boucle de parcours du fichier fonctionnera toujours :
nbre_lignes = -1
for nbre_lignes, ligne in enumerate(open(chemin_fichier, 'rU')):
pass
nbre_lignes += 1
Voici une autre solution astucieuse, potentiellement plus rapide pour les fichiers vraiment énormes lorsque la fin de ligne est '\n' (ou contient '\n', comme avec
Windows) :
nbre_lignes = 0
fichier = open(chemin_fichier, 'rb')
while True:
tampon = fichier.read(8192*1024)
if not tampon:
break
nbre_lignes += tampon.count('\n')
fichier.close()
Si vous recherchez la vitesse, le paramètre 'rb' de open est nécessaire ; sans lui, ce
code serait très lent sous Windows.
Discussion
Si vous disposez déjà d’un programme externe qui compte les lignes d’un fichier,
comme wc -l sur les plates-formes Unix, vous pouvez bien sûr l’utiliser (via os.popen,
par exemple). Cependant, il est généralement plus simple, plus rapide et plus portable
de compter les lignes avec votre propre programme. Vous pouvez considérer que
quasiment tous les fichiers texte ont une taille raisonnable, ce qui vous autorise à
transférer d’un seul coup tout leur contenu en mémoire. En ce cas, la longueur du
résultat de readlines, renvoyée par len, vous donnera le nombre de lignes de la
façon la plus simple qui soit.
Si le fichier est plus gros que la mémoire disponible (quelques centaines de mégaoctets sur un PC actuel), cette solution peut devenir trop lente car le système
d’exploitation devra lutter pour faire tenir le contenu du fichier dans la mémoire virtuelle. Il peut même échouer si la mémoire virtuelle n’est pas suffisante. Sur un PC
classique doté de 256 Mo de RAM et d’un espace disque quasiment illimité, vous pouvez quand même rencontrer de sérieux problèmes si vous tentez de lire en mémoire
des fichiers dépassant un ou deux giga-octets (certains systèmes d’exploitation sont
bien plus fragiles que d’autres dans la gestion des problèmes de mémoire virtuelle lors
de situations épineuses comme celle-ci). En ce cas, il est préférable de boucler sur
l’objet fichier, comme on le montre dans la solution de cette recette. La fonction
prédéfinie enumerate mémorise le nombre de lignes sans que votre code ait à le faire
explicitement.
L’idée maîtresse de la troisième approche consiste à compter les caractères de fin de
ligne pendant la lecture octet par octet du fichier dans des blocs de taille raisonnable.
C’est sûrement la moins intuitive au premier abord et elle n’est pas totalement
portable, mais c’est probablement la plus rapide (si on la compare, par exemple, à la
recette 8.2 de Perl en action).
72
Chapitre 2 — Fichiers
Cependant, dans la plupart des cas, les performances ne sont pas vraiment un problème. Si elles le devenaient, la partie de votre programme qui consomme le plus de
temps d’exécution ne sera peut-être pas celle à laquelle vous pensez : ne faites jamais
confiance à votre intuition à ce sujet — faites toujours des mesures et des comparaisons des temps d’exécution. Considérons, par exemple, un fichier syslog classique
d’Unix, de taille moyenne (un peu plus de 18 Mo répartis sur 230000 lignes de texte) :
[situ@tioni nuc]$ wc nuc
231581 2312730 18508908 nuc
Soit le script de test et de mesure des temps d’exécution, nommé bench.py :
import time
def chrono(fction, n = 10):
debut = time.clock()
for i in xrange(n): fction()
fin = time.clock()
temps = fin - debut
return fction.__name__, temps
import os
def compter_lignes_w():
return int(os.popen('wc -l nuc').read().split()[0])
def compter_lignes_1():
return len(open('nuc').readlines())
def compter_lignes_2():
nbre_lignes = -1
for nbre_lignes, ligne in enumerate(open('nuc')): pass
return nbre_lignes + 1
def compter_lignes_3():
nbre_lignes = 0
fichier = open('nuc', 'rb')
while True:
tampon = fichier.read(65536)
if not tampon: break
nbre_lignes += tampon.count('\n')
return nbre_lignes
for f in compter_lignes_w, compter_lignes_1, compter_lignes_2, compter_lignes_3:
print f.__name__, f()
for f in compter_lignes_1, compter_lignes_2, compter_lignes_3:
print "%s: %.2f" % chrono(f)
Le programme affiche d’abord les nombres de lignes obtenus par toutes les solutions,
afin de vérifier qu’aucune anomalie ou erreur ne se produit (les opérations de
comptage sont connues pour être sujettes aux erreurs de type « un en trop » ou « un
en moins »). Puis, il lance 10 fois chaque solution sous le contrôle de la fonction de
chronométrage chrono afin d’examiner les résultats que voici, tels qu’ils ont été produits sur une machine ancienne mais fiable :
[situ@tioni nuc]$ python -O bench.py
compter_lignes_w 231581
compter_lignes_1 231581
compter_lignes_2 231581
compter_lignes_3 231581
Traiter tous les mots d’un fichier
73
compter_lignes_1: 4.84
compter_lignes_2: 4.54
compter_lignes_3: 5.02
Comme vous pouvez le constater, les écarts de performance sont faibles : vos
utilisateurs ne remarqueront jamais une différence d’environ 10% dans une tâche
annexe. Cependant, l’approche la plus rapide (dans mon cas particulier, sur une
machine ancienne utilisant une distribution classique de Linux et pour cette mesure
spécifique) est l’humble technique consistant à lire ligne par ligne, tandis que la plus
lente est l’approche rusée, ambitieuse, consistant à compter les fins de lignes au cours
d’une lecture bloc par bloc du fichier. En pratique, sauf si je dois gérer des fichiers de
plusieurs centaines de méga-octets, j’utilise toujours l’approche la plus simple, qui est
la première présentée dans cette recette.
Plutôt qu’utiliser aveuglément des approches compliquées en espérant qu’elles seront
plus rapides, il est très important de mesurer les temps d’exécution exacts des portions
de codes — c’est si important que la bibliothèque standard de Python fournit un
module, timeit, spécialement conçu à cet effet. Je vous conseille de l’utiliser plutôt
que de coder vos propres fonctions de mesures comme je l’ai fait ici. En fait, j’ai écrit
cette fonction il y a bien longtemps, bien avant que timeit n’apparaisse : je crois donc
que je peux être pardonné de ne pas utiliser timeit dans ce cas précis !
Voir aussi
Les sections sur les objets fichiers, sur la fonction prédéfinie enumerate, sur os.popen
et sur les modules time et timeit dans la Library Reference et dans Python en concentré ;
la recette 8.2 de Perl en action.
2.6 Traiter tous les mots d’un fichier
Par Luther Blissett
Problème
Vous devez appliquer un traitement à chaque mot d’un fichier.
Solution
Pour effectuer cette opération, le plus simple consiste à utiliser deux boucles imbriquées, une pour parcourir les lignes, une autre pour parcourir les mots de chaque
ligne :
for ligne in open(chemin_fichier):
for mot in ligne.split():
traiter(mot)
L’en-tête de l’instruction for interne définit implicitement les mots comme des
séquences de caractères non espace séparés par des séquences d’espaces (comme la
commande wc d’Unix). Si vous préférez donner d’autres définitions aux mots, vous
pouvez utiliser des expressions régulières :
74
Chapitre 2 — Fichiers
import re
re_mot = re.compile(r"[\w'-]+")
for ligne in open(chemin_fichier):
for mot in re_mot.finditer(ligne):
traiter(mot.group(0))
Ici, un mot est défini comme une suite de caractères alphanumériques, de tirets et
d’apostrophes.
Discussion
Pour d’autres définitions des mots, vous utiliserez évidemment d’autres expressions
régulières. La boucle externe, qui parcourt toutes les lignes du fichier, ne changera
pas.
Il est souvent souhaitable d’envelopper les itérations dans des objets itérateurs ; le
plus souvent, on réalisera ce type d’encapsulation à l’aide d’un simple générateur :
def mots_du_fichier(chemin_fichier, ligne_en_mots = str.split):
fichier = open(chemin_fichier):
for ligne in fichier:
for mot in ligne_en_mots(ligne):
yield mot
fichier.close()
for mot in mots_du_fichier(chemin_fichier):
traiter(mot)
Cette approche permet de séparer, proprement et efficacement, deux problèmes différents : le parcours de tous les éléments (ici les mots d’un fichier) et ce qu’il convient
de faire avec chaque élément de l’itération. Lorsque les problèmes d’itération ont été
encapsulés dans un objet itérateur (souvent, comme ici, un générateur), la plupart des
utilisations de l’itération deviennent de simples instructions for. Vous pouvez souvent réutiliser l’itérateur en de nombreux points de votre programme et, si vous
devez corriger ce code, vous n’aurez besoin de n’intervenir qu’à un seul endroit
— dans la définition de l’itérateur — plutôt que de devoir partir à la recherche de
tous les endroits où il est utilisé. Les avantages sont donc très similaires à ceux obtenus dans n’importe quel autre langage de programmation lorsque l’on définit et utilise des fonctions de façon appropriée au lieu de copier et coller des portions de code
un peu partout. Avec les itérateurs de Python, vous pouvez également bénéficier de
cet avantage de la réutilisation pour toutes vos structures de boucles.
Nous avons profité de l’opportunité fournie par l’encapsulation de la boucle dans un
générateur pour apporter deux petites améliorations — on ferme explicitement le
fichier, ce qui est toujours conseillé et on généralise la façon dont chaque ligne est
découpée en mots (on utilise par défaut la méthode split des objets chaînes mais il
est possible de choisir une autre méthode). Grâce à ce « hook », nous pouvons écrire
une autre enveloppe autour de mots_du_fichier si, par exemple, nous souhaitons
définir les mots par une expression régulière :
import re
def mots_par_re(chemin_fichier, motif = r"[\w'-]+"):
re_mot = re.compile(motif)
def ligne_en_mots(ligne):
for mo in re_mot.finditer(ligne):
Utiliser des entrées/sorties à accès direct
75
return mo.group(0)
return mots_du_fichier(chemin_fichier, ligne_en_mots)
Ici aussi, nous fournissons une expression régulière par défaut pour définir un mot
tout en laissant la possibilité d’en passer une autre dans les cas où une autre définition
serait nécessaire. Une généralisation excessive est une tentation pernicieuse mais,
employée à bon escient, une généralisation guidée par l’expérience remboursera souvent amplement le petit effort consenti pour la mettre en place. L’un des moyens les
plus simples et les plus commodes pour implémenter ce type de généralisation
consiste à utiliser une fonction qui attend un paramètre facultatif valant par défaut
la valeur la plus probable.
Voir aussi
Le chapitre 11 pour plus d’informations sur les itérateurs et les générateurs ; les sections de la Library Reference et de Python en concentré consacrées aux objets fichiers et
au module re ; la recette 8.3 de Perl en action.
2.7 Utiliser des entrées/sorties à accès direct
Par Luther Blissett
Problème
Vous voulez lire un enregistrement binaire situé quelque part dans un gros fichier
formé d’enregistrements de taille fixe, sans devoir lire tous les enregistrements qui le
précèdent.
Solution
Sachant que la position d’octet du début d’un enregistrement dans le fichier est la
taille d’un enregistrement en octets multipliée par le nombre d’enregistrements qui
le précèdent (en partant de 0), vous pouvez atteindre directement le bon emplacement puis lire les données. Pour, par exemple, lire le septième enregistrement d’un
fichier binaire formé d’enregistrements de 48 octets :
fichier = open('fichier_binaire', 'rb')
taille_enreg = 48
num_enreg = 6
fichier.seek(taille_enreg * num_enreg)
tampon = fichier.read(taille_enreg)
Vous remarquerez que le numéro du septième enregistrement est 6 puisque cette
numérotation commence à zéro !
Discussion
Cette approche ne fonctionne qu’avec des fichiers (généralement binaires) formés
d’enregistrements de même taille ; elle ne convient pas aux fichiers texte classiques.
La recette ouvre le fichier en mode binaire en passant 'rb' comme second paramètre
de open, juste avant l’appel à seek, mais c’est uniquement pour clarifier la situation :
tant que l’objet fichier est ouvert en lecture binaire, vous pouvez effectuer autant
76
Chapitre 2 — Fichiers
d’opérations seek et read que vous le souhaitez avant de le fermer — il n’est pas
nécessaire d’ouvrir le fichier juste avant d’effectuer un seek.
Voir aussi
La section sur les objets fichiers de la Library Reference et de Python en concentré ; la
recette 8.12 de Perl en action.
2.8 Modifier un fichier à accès direct
Par Luther Blissett
Problème
Vous voulez lire un enregistrement binaire dans un gros fichier contenant des enregistrements de taille fixe, modifier un ou plusieurs champs de cet enregistrement puis
le réécrire dans le fichier.
Solution
Lisez l’enregistrement, décompactez-le, effectuez les traitements correspondant aux
modifications, ré-empaquetez les champs dans l’enregistrement, positionnez-vous sur
le début de l’enregistrement dans le fichier et réécrivez-le... Rassurez-vous, c’est plus
rapide à coder qu’à énoncer :
import struct
chaine_format = '8l' # pour un enregistrement de 8 entiers de 4 octets
fichier = open('fichier_binaire', 'r+b')
taille_enreg = struct.calcsize(chaine_format)
num_enreg = 6
fichier.seek(taille_enreg * num_enreg)
tampon = fichier.read(taille_enreg)
champs = list(struct.unpack(chaine_format, tampon))
# Effectue les traitements, modifie les champs puis :
tampon = struct.pack(chaine_format, *champs)
fichier.seek(taille_enreg * num_enreg)
fichier.write(tampon)
fichier.close()
Discussion
Cette approche ne fonctionne qu’avec des fichiers (généralement binaires) contenant
des enregistrements ayant tous la même taille ; elle ne convient pas aux fichiers texte
classiques. En outre, la taille de chaque enregistrement doit être définie par une
chaîne de format struct, comme le montre le code de la recette. La chaîne de format
'8l', par exemple, indique que chaque enregistrement est formé de 8 entiers de quatre octets, chacun devant être interprété comme une valeur signée et décompacté
dans un int Python. Ici, la variable champs de la recette serait donc liée à une liste
de huit int. La fonction struct.unpack renvoie un tuple : les tuples étant non
modifiables, le calcul devrait donc réaffecter la variable champs complète. Une liste,
par contre, est modifiable et l’on peut donc modifier chaque champ individuellement,
Modifier un fichier à accès direct
77
c’est la raison pour laquelle on demande explicitement une liste lorsque l’on affecte
champs. Prenez garde, cependant, à ne pas modifier la longueur de la liste : ici, elle
ne doit comporter que huit entiers, sinon struct.pack déclenchera une exception si
on l’appelle avec une chaine_format de '8l'. N’oubliez pas, cette recette ne fonctionne pas lorsque les enregistrements ne sont pas tous de la même taille.
Pour revenir au début de l’enregistrement, vous pouvez effectuer un déplacement
relatif au lieu d’utiliser le déplacement taille_enreg * num_enreg par rapport au
début du fichier :
fichier.seek(-taille_enreg, 1)
Le second paramètre de la méthode seek (1) demande à l’objet fichier de se déplacer
par rapport à la position courante (ici, un déplacement vers le début du fichier puisque le premier paramètre est négatif). Par défaut, seek effectue un déplacement
absolu (à partir du début du fichier), mais vous pouvez également demander
explicitement ce comportement par défaut en appelant seek avec un second
paramètre valant 0.
Il n’est pas nécessaire d’ouvrir le fichier juste avant le premier seek, ni de le fermer
juste après l’appel à write. Lorsqu’un objet fichier a été correctement ouvert (en mise
à jour et en mode binaire, pas en mode texte), vous pouvez le modifier autant de fois
que vous le souhaitez avant de le refermer. Ces appels ne sont montrés ici que pour
insister sur le mode d’ouverture approprié aux mises à jour en accès direct et sur
l’importance de refermer un fichier lorsqu’on n’a plus besoin de le manipuler.
Le fichier doit être ouvert en mise à jour (il doit pouvoir être lu et écrit). C’est ce que
signifie le paramètre 'r+b' de l’appel à open : ouverture en lecture et en écriture, sans
appliquer de transformations implicites sur le contenu car c’est un fichier binaire. Sur
les systèmes Unix ou apparentés, la partie 'b' n’est pas nécessaire, mais elle est
conseillée pour améliorer la lisibilité ; elle est essentielle sur les autres plates-formes
comme Windows. Si vous créez le fichier binaire en partant de zéro, mais que vous
voulez quand même pouvoir revenir en arrière, relire et modifier des enregistrements
sans devoir fermer et rouvrir le fichier, vous pouvez utiliser plutôt 'w+b' comme
deuxième paramètre. Cela dit, je n’ai jamais rencontré cette étrange combinaison
d’exigences ; les fichiers binaires sont généralement d’abord créés (en les ouvrant avec
'wb', en écrivant des données et en fermant le fichier) pour être réouverts plus tard
avec 'r+b' lorsqu’on souhaite les modifier.
Bien que cette approche ne soit généralement utile qu’avec un fichier dont tous les
enregistrements sont de la même taille, on peut également atteindre directement des
enregistrements à l’aide d’une méthode plus élaborée : en utilisant un « fichier
index » fournissant la position et la longueur de chaque enregistrement dans le
« fichier des données ». Ce type d’accès séquentiel indexé n’est plus tellement à la
mode mais, à une époque, il était très utilisé. De nos jours, on ne s’occupe plus guère
que de fichiers texte (de différentes sortes, le plus souvent en XML), de bases de données et, à l’occasion, de fichiers binaires contenant des enregistrements de taille fixe.
Cependant, si vous avez besoin d’accéder à un fichier binaire séquentiel indexé, le
code sera assez similaire à celui présenté dans cette recette, sauf que vous devrez obtenir la taille_enreg et le paramètre de déplacement passé à fichier.seek en les lisant
dans le fichier index au lieu de les calculer vous-même comme dans cette recette.
78
Chapitre 2 — Fichiers
Voir aussi
Les sections de la Library Reference et de Python en concentré consacrées aux objets
fichiers et au module struct ; la recette 8.13 de Perl en action.
2.9 Lire des données contenues dans un fichier zip
Par Paul Prescod et Alex Martelli
Problème
Vous voulez examiner directement un ou plusieurs fichiers contenus dans une archive
au format zip, sans les décompacter sur le disque.
Solution
Les fichiers zip sont des fichiers archives très employés et reconnus sur toutes les plates-formes. La bibliothèque standard de Python fournit un module zipfile permettant d’accéder facilement à ce type de fichier :
import zipfile
z = zipfile.ZipFile("archive.zip", "r")
for nomfic in z.namelist():
print 'Le fichier', nomfic,
nb_octets = z.read(nomfic)
print 'contient', len(nb_octets), 'octets.'
Discussion
Python peut travailler directement sur les données contenues dans les fichiers zip.
Vous pouvez examiner la liste des éléments du répertoire de l’archive et travailler sur
les « fichiers de données » eux-mêmes. Dans cette recette, nous affichons les noms et
les tailles des fichiers contenus dans l’archive zip nommée archive.zip.
Le module zipfile ne sait pas encore gérer les fichiers zip multi-volumes, ni ceux qui
contiennent des commentaires. Faites attention à bien utiliser le paramètre r et pas
rb, qui pourrait pourtant sembler plus naturel (sous Windows, par exemple). Avec
ZipFile, en effet, ce paramètre n’est pas utilisé de la même façon que dans l’ouverture d’un fichier et rb n’est pas reconnu. Le paramètre r gère la nature binaire intrinsèque de tous les fichiers zip sur toutes les plates-formes.
Si un fichier zip contient des modules Python (donc des fichiers .py ou, mieux, .pyc)
en plus d’autres fichiers de données, vous pouvez ajouter le chemin de cette archive
au sys.path de Python et utiliser ensuite l’instruction import pour importer ces
modules. Voici un exemple purement académique démontrant comment créer à la
volée un fichier zip, importer un module qu’il contient puis le supprimer — juste
pour vous montrer comment faire :
import zipfile, tempfile, os, sys
descfic, nomfic = tempfile.mkstemp('.zip')
os.close(descfic)
z = zipfile.ZipFile(nomfic, 'w')
z.writestr('bonjour.py', 'def f(): return "Bonjour de " + __file__\n')
Gérer un fichier zip dans une chaîne
79
z.close()
sys.path.insert(0, nomfic)
import bonjour
print bonjour.f()
os.unlink(nomfic)
L’exécution de ce script produira une ligne comme celle-ci :
Bonjour de /tmp/tmpJ-pXnH.zip/bonjour.py
Outre qu’il illustre la possibilité d’importer un module à partir d’un fichier zip, cet
exemple montre également comment créer (et ensuite supprimer) un fichier temporaire et comment utiliser la méthode writestr pour ajouter un élément à une archive
zip sans l’écrire d’abord dans un fichier sur disque.
Vous remarquerez que le chemin d’accès au fichier zip à partir duquel vous faites
l’import est traité un peu comme un répertoire (dans notre exemple d’exécution, ce
chemin est /tmp/tmpJ-pXnH.zip mais, bien sûr, cette valeur peut changer à chaque
exécution puisque nous utilisons un fichier temporaire et qu’il dépend également de
votre plate-forme). La variable globale __file__ du module bonjour qui est importé
du fichier zip, notamment, contient la valeur /tmp/tmpJ-pXnH.zip/bonjour.py — un
pseudo-chemin composé du chemin d’accès au fichier zip considéré comme un « répertoire », suivi du chemin relatif de bonjour.py dans cette archive. Si vous importez d’un
fichier zip un module traitant les chemins relatifs par rapport à lui-même pour obtenir les fichiers de données, vous devrez adapter le module. En effet, vous ne pouvez
pas simplement ouvrir avec open un tel « pseudo-chemin » pour obtenir un objet
fichier : pour lire ou écrire des fichiers contenus dans une archive zip, vous devez utiliser les fonctions du module standard zipfile, comme on l’a montré dans la solution.
Voir aussi
La documentation des modules zipfile, tempfile, os et sys dans la Library Reference
et dans Python en concentré ; la recette 2.11 explique comment archiver une
arborescence de fichiers.
2.10 Gérer un fichier zip dans une chaîne
Par Indyana Jones
Problème
Votre programme reçoit un fichier zip sous la forme d’une chaîne d’octets en
mémoire et vous devez lire les informations contenues dans cette archive.
Solution
Le module cStringIO de la bibliothèque standard est conçu exactement pour régler
ce type de problème :
import cStringIO, zipfile
class ZipString(ZipFile):
def __init__(self, chaine_donnees):
ZipFile.__init__(self, cStringIO.StringIO(chaine_donnees))
80
Chapitre 2 — Fichiers
Discussion
J’ai souvent été confronté à des fichiers zip provenant, par exemple, de champs BLOB
d’une base de données ou reçus via une connexion réseau. Pour résoudre ce problème, j’avais pris l’habitude de sauvegarder ces données binaires dans un fichier temporaire que j’ouvrais ensuite avec le module standard zipfile. Je devais, bien sûr,
vérifier que le fichier temporaire était détruit à la fin de l’opération. J’ai ensuite pensé
à utiliser le module cStringIO pour cette tâche... et je ne suis jamais revenu en
arrière.
Le module cStringIO permet d’envelopper une chaîne d’octets pour qu’elle soit
manipulée comme un objet fichier. Vous pouvez aussi effectuer des opérations telles
qu’écrire dans une instance de cStringIO.StringIO comme s’il s’agissait d’un objet
fichier et éventuellement récupérer son contenu sous la forme d’une chaîne d’octets.
La plupart des modules Python manipulant des objets fichiers ne testent pas si vous
leur passez un vrai fichier — tout objet apparenté à un fichier fait l’affaire ; le code du
module se contente d’appeler sur l’objet les méthodes de fichiers dont il a besoin.
Tant que l’objet fournit ces méthodes et qu’il répond correctement lorsqu’elles sont
appelées, tout fonctionne très bien. Cet exemple démontre la puissance effrayante du
polymorphisme par signature et explique pourquoi vous ne devriez quasiment jamais
tester les types (écrire des abominations comme if type(x) is y ou même l’horreur
un peu moins terrible if isinstance(x, y)) dans votre code ! Quelques modules de
bas niveau, comme marshal, sont malheureusement inflexibles et exigent de « vrais »
fichiers, mais zipfile n’en fait pas partie et cette recette montre comment vous simplifier la vie !
Le c au début du nom du module cStringIO indique qu’il s’agit d’un module spécifique au versions de Python écrites en C (CPython), optimisé pour la vitesse mais
pas nécessairement inclus dans la bibliothèque standard des autres implémentations.
Parmi ces implémentations alternatives, on en trouve qui peuvent être utilisées en
production (comme Jython, qui est codé en Java et s’exécute sur une JVM) et d’autres,
qui sont au stade expérimental (comme pypy, qui est codé en Python et qui produit
du code machine, ainsi que IronPython, qui est codé en C# et s’exécute sur .NET de
Microsoft). Cependant, la bibliothèque standard de Python contient toujours le
module StringIO, codé en Python pur (par conséquent utilisable avec n’importe
quelle implémentation de Python). Ce module implémente la même fonctionnalité
que cStringIO (mais peut-être un peu plus lentement que la version de CPython). Il
suffit donc de modifier légèrement votre instruction import pour importer cStringIO
s’il est disponible ou StringIO sinon. Cette recette pourrait, par exemple, être réécrite
de cette façon :
import zipfile
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class ZipString(ZipFile):
def __init__(self, chaine_donnees):
ZipFile.__init__(self, StringIO(chaine_donnees))
Avec cette modification, la recette peut fonctionner avec Jython et toutes les autres
implémentations.
Archiver une arborescence de fichiers dans une archive tar compressée
81
Voir aussi
La documentation sur les modules zipfile et cStringIO dans la Library Reference et
dans Python en concentré ; le site consacré à Jython, http://www.jython.org/ ; le site
consacré à pypy, http://codespeak.net/pypy/ ; le site consacré à IronPython, http://
ironpython.com/.
2.11 Archiver une arborescence de fichiers
dans une archive tar compressée
Par Ed Gordon et Ravi Teja Bhupatiraju
Problème
Vous devez archiver tous les fichiers et répertoires d’une arborescence dans un fichier
tar, en compressant les données avec le programme gzip bien connu ou avec le programme bzip2, qui garantit un fort taux de compression.
Solution
Le module tarfile de la bibliothèque standard reconnaît ces deux types de compression : il suffit de passer en paramètre à tarfile.TarFile.open la compression que
vous souhaitez utiliser lorsque vous créez le fichier archive. Voici un exemple :
import tarfile, os
def creer_tar(rep_a_sauvegarder, rep_dest, compression = 'bz2'):
if compression:
extension_dest = '.' + compression
else:
extension_dest_ext = ''
nom_archive = os.path.basename(rep_a_sauvegarder)
nom_dest = '%s.tar%s' % (nom_archive, extension_dest)
chemin_dest = os.path.join(rep_dest, nom_dest)
if compression:
comp_dest = ':' + compression
else:
comp_dest = ''
resultat = tarfile.TarFile.open(chemin_dest, 'w' + comp_dest)
resultat.add(rep_a_sauvegarder, nom_archive)
resultat.close()
return chemin_dest
Discussion
Le paramètre compression de la fonction creer_tar peut recevoir la chaîne 'gz' pour
obtenir une compression par gzip au lieu de la compression bzip2 par défaut ou la
chaîne vide si vous n’en souhaitez aucune. Il permet de créer une extension de fichier
appropriée (.tar, .tar.gz ou .tar.bz2) et sert à déterminer la chaîne qui sera passée au
deuxième paramètre de tarfile.TarFile.open : 'w' si l’on ne souhaite aucune compression, 'w:gz' ou 'w:bz2' pour obtenir les deux types de compression.
Outre open, la classe tarfile.TarFile offre plusieurs autres classmethod permettant
de créer une instance adaptée à vos besoins. Je trouve que open est plus pratique et
82
Chapitre 2 — Fichiers
plus souple car on peut lui passer des informations sur la compression via son
paramètre mode. Cependant, si vous voulez être sûr que la compression bzip2 sera
toujours utilisée, par exemple, vous pourriez appeler plutôt bz2open.
Une fois que l’on dispose d’une instance de la classe tarfile.TarFile configurée avec
le type de compression choisi, la méthode add de cette instance suffit à nos besoins.
Lorsque la chaîne rep_a_sauvegarder contient un nom de répertoire au lieu d’un
nom de fichier, notamment, add ajoute récursivement toute l’arborescence partant de
ce répertoire. Si, pour une autre occasion, nous voulons modifier ce comportement
pour disposer d’un contrôle précis sur ce qui est archivé, nous pouvons passer à add
le paramètre nommé supplémentaire recursive = False pour empêcher cette
récursivité implicite. Après l’appel à add, il suffit que la fonction creer_tar ferme
l’instance de TarFile et renvoie le chemin d’accès du fichier tar qui a été créé, au cas
où l’appelant aurait besoin de cette information.
Voir aussi
La documentation sur le module tarfile dans la Library Reference.
2.12 Envoyer des données binaires sur la sortie
standard de Windows
Par Hamish Lawson
Problème
Vous voulez envoyer des données binaires (une image, par exemple) sur stdout avec
Windows.
Solution
La fonction setmode du module msvcrt de la bibliothèque standard, spécifique à
Windows, a été conçue pour cela :
import sys
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
Vous pouvez maintenant appeler sys.stdout.write avec n’importe quelle chaîne
d’octets en paramètre afin qu’elle soit produite sans modification sur la sortie standard.
Discussion
Bien qu’Unix ne fasse pas de différence entre les modes texte et binaire (il n’a pas
besoin de la faire), Windows vous impose d’ouvrir un fichier en mode binaire si vous
souhaitez lire des informations binaires ou y écrire des données binaires, comme des
images. C’est un problème pour les programmes qui écrivent des données binaires sur
la sortie standard (un script CGI, par exemple) car Python ouvre l’objet fichier
sys.stdout pour vous, généralement en mode texte.
Utiliser une syntaxe comme celle de iostream en C++
83
Vous pouvez ouvrir stdout en mode binaire en utilisant l’option -u de la ligne de
commande de l’interpréteur Python. Si vous utilisez Python 2.3 avec une installation
standard et que vous savez, par exemple, que votre script CGI s’exécutera sur un serveur web Apache, vous pouvez le débuter par une ligne comme celle-ci :
#! c:/python23/python.exe -u
Malheureusement, vous ne pourrez pas toujours contrôler la ligne de commande sous
laquelle votre script s’exécutera. L’approche utilisée par la solution de cette recette
offre une alternative : la fonction setmode fournie par le module msvcrt spécifique à
Windows permet de changer le mode du descripteur de fichier sous-jacent de stdout.
Grâce à elle, vous pouvez être sûr que votre programme utilisera sys.stdout en mode
binaire.
Voir aussi
La documentation du module msvcrt dans la Library Reference et dans Python en
concentré.
2.13 Utiliser une syntaxe comme celle de iostream
en C++
Par Erik Max Francis
Problème
Vous appréciez l’approche de C++ pour les entrées/sorties, qui repose sur des ostream
et des manipulateurs (objets spéciaux provoquant des effets particuliers sur un flux
lorsqu’il y sont insérés) et vous voulez l’utiliser dans vos programmes Python.
Solution
Python permet de surcharger les opérateurs en définissant des méthodes spéciales
dans vos classes (des méthodes dont les noms débutent et se terminent par deux
blancs soulignés). Pour utiliser << pour les sorties, comme en C++, il suffit de créer
une classe pour les flux de sortie qui définit la méthode spéciale __lshift__ :
# -*- coding: utf-8 -*class IOManipulator(object):
def __init__(self, fonction = None):
self.fonction = fonction
def do(self, sortie):
self.fonction(sortie)
def do_endl(flux):
flux.sortie.write('\n')
flux.sortie.flush()
endl = IOManipulator(do_endl)
class OStream(object):
def __init__(self, sortie = None):
if sortie is None:
import sys
sortie = sys.stdout
84
Chapitre 2 — Fichiers
self.sortie = sortie
self.format = '%s'
def __lshift__(self, truc):
''' Méthode spéciale appelée par Python lorsqu'on utilise
l'opérateur << avec une opérande gauche de type OStream '''
if isinstance(truc, IOManipulator):
truc.do(self)
else:
self.sortie.write(self.format % truc)
self.format = '%s'
return self
def exemple_main():
cout = OStream()
cout << "La moyenne de " << 1 << " et " << 3 << " est " << (1+3)/2 << endl
# Produit la moyenne de 1 et 3 est 2
if __name__ == '__main__':
exemple_main()
Discussion
Avec Python, il est assez facile d’envelopper des objets apparentés à des fichiers afin
de simuler la syntaxe des ostream de C++. Cette recette montre comment coder l’opérateur d’insertion << dans ce but. Elle implémente également une classe
IOManipulator (comme en C++) permettant d’appeler des fonctions quelconques sur
un flux lors de son insertion, ainsi qu’un manipulateur prédéfini endl (devinez d’où
vient son nom) pour produire un retour à la ligne et vider le flux.
Les instances de la classe OStream contiennent un attribut format et le réinitialisent
à la valeur par défaut '%s' après chaque appel à self.sortie.write : vous pouvez
donc détourner des manipulateurs pour qu’ils sauvegardent temporairement le
formatage de l’objet flux. Voici un exemple :
#-*- coding: utf-8 -*def do_hex(stream):
stream.format = '%x'
hex = IOManipulator(do_hex)
cout << 23 << ' en hexadécimal = ' << hex << 23 << ', en décimal = ' << 23 <<
endl
# Produit : 23 en hexadécimal = 17, en décimal = 23
Certains détestent la syntaxe cout << qquechose de C++, d’autres l’adorent. Dans les
cas comme celui de l’exemple de la recette, cette syntaxe est quand même plus simple
et plus lisible que :
print >> qquepart, "La moyenne de %d et %d est %f\n" % (1, 3, (1+3)/2)
qui est la solution « native » de Python (et qui ressemble beaucoup à C dans ce cas
précis), même si cela dépend en partie du fait que vous soyez habitués à C++ ou à C.
Quoi qu’il en soit, cette recette vous donne le choix ! Même si vous n’utiliserez jamais
cette approche, cette recette a permis de montrer la simplicité avec laquelle on peut
surcharger un opérateur en Python.
Revenir au début d’un fichier d’entrée
85
Voir aussi
La documentation sur les objets fichiers et les méthodes spéciales comme __lshift__
dans la Library Reference et dans Python en concentré ; la recette 4.20 implémente une
version Python de la fonction printf du langage C.
2.14 Revenir au début d’un fichier d’entrée
Par Andrew Dalke
Problème
Vous voulez revenir au début d’un objet fichier d’entrée (contenant des données provenant d’une socket ou d’un autre descripteur de fichier ouvert en lecture) pour pouvoir le relire.
Solution
Enveloppez l’objet fichier dans une classe adaptée :
#-*- coding: utf-8 -*from cStringIO import StringIO
class RewindableFile(object):
""" Enveloppe un descripteur de fichier pour pouvoir revenir au début. """
def __init__(self, fichier_entree):
""" Enveloppe fichier_entree dans un objet apparenté à un fichier
avec rembobinage. """
self.fichier = fichier_entree
self.tampon_fichier = StringIO()
self.au_debut = True
try:
self.debut = fichier_entree.tell()
except (IOError, AttributeError):
self.debut = 0
self._utilise_tampon = True
def seek(self, deplacement, a_partir_de = 0):
""" Positionnement sur une position d'octet.
Doit être : a_partir_de == 0 et deplacement == self.debut
"""
if a_partir_de != 0:
raise ValueError("whence= %r ; valeur attendue 0" % (a_partir_de,))
if deplacement != self.debut:
raise ValueError("deplacement = %r ; valeur attendue %s" % (offset,
self.debut))
self.rewind()
def rewind(self):
""" Méthode simple pour revenir au début. """
self.tampon_fichier.seek(0)
self.au_debut = True
def tell(self):
""" Renvoie la position courante dans le fichier (doit être au
début). """
86
Chapitre 2 — Fichiers
def
def
def
def
def
if not self.au_debut:
raise TypeError("RewindableFile ne peut renvoyer que le début du
fichier")
return self.debut
_read(self, taille):
if taille < 0:
# Lit tout jusqu'à la fin du fichier
y = self.fichier.read()
if self._utilise_tampon:
self.tampon_fichier.write(y)
return self.tampon_fichier.read() + y
elif taille == 0:
# Inutile de lire la chaîne vide
return ""
x = self.tampon_fichier.read(taille)
if len(x) < taille:
y = self.fichier.read(taille - len(x))
if self._utilise_tampon:
self.tampon_fichier.write(y)
return x + y
return x
read(self, taille = -1):
""" Lit jusqu'à 'taille' octets du fichier.
La taille par défaut est -1, qui indique de lire jusqu'à la fin.
"""
x = self._read(taille)
if self.au_debut and x:
self.au_debut = False
self._verif_pas_de_tampon()
return x
readline(self):
""" Lit une ligne du fichier. """
# Peut-on l'obtenir à partir de tampon_fichier ?
s = self.tampon_fichier.readline()
if s[-1:] == "\n":
return s
# Non, on lit une ligne dans le fichier d'entrée.
t = self.fichier.readline()
if self._utilise_tampon:
self.tampon_fichier.write(t)
self._verif_pas_de_tampon()
return s + t
readlines(self):
"""Lit toutes les lignes restantes dans le fichier."""
return self.read().splitlines(True)
_verif_pas_de_tampon(self):
# Si 'pas_de_tampon' a été appelée et que l'on n'a plus besoin du tampon,
# on le supprime et on redirige tout vers le fichier d'entrée initial.
if not self._utilise_tampon and \
self.tampon_fichier.tell() == len(self.tampon_fichier.getvalue()):
# Pour de meilleures performances, on lie à nouveau toutes les
# méthodes concernées dans self
for n in 'seek tell read readline readlines'.split():
setattr(self, n, getattr(self.fichier, n, None))
del self.tampon_fichier
Revenir au début d’un fichier d’entrée
87
def pas_de_tampon(self):
"""Demande à RewindableFile d'arrêter d'utiliser le tampon une fois
qu'il est plein"""
self._utilise_tampon = False
Discussion
Parfois, les données provenant d’une socket ou d’un autre descripteur de fichier ne
sont pas celles attendues. Supposons, par exemple, que vous lisiez des données sur un
serveur bogué censé renvoyer un flux XML, mais produisant parfois un message
d’erreur non formaté (ce scénario arrive souvent car de nombreux serveurs ne gèrent
pas très bien les entrées incorrectes).
La classe RewindableFile de cette recette permet de résoudre ce problème. r =
RewindableFile(f) enveloppe f, le flux d’entrée initial, dans une instance de « fichier
rembobinable », r, qui imite essentiellement le comportement de f tout en fournissant également un tampon. Les requêtes de lecture adressées à r sont transmises à f
et les données lues sont ajoutées à un tampon qui est ensuite renvoyé à l’appelant.
Ce tampon contient toutes les données qui ont été lues.
On peut demander à r de se rembobiner avec rewind, c’est-à-dire de revenir à la position de départ. La prochaine requête de lecture lira alors les données dans le tampon,
jusqu’à ce que celui-ci ait été totalement lu, auquel cas les données viendront à nouveau du flux d’entrée. Les nouvelles données lues sont également ajoutées au tampon.
Lorsque vous n’avez plus besoin du tampon, appelez la méthode pas_de_tampon de r.
Celle-ci indique à r qu’il peut se débarrasser du tampon après avoir lu son contenu.
Après l’appel de cette méthode, le comportement de seek n’est plus défini.
Supposons, par exemple, que votre serveur produise soit un message d’erreur de la
forme ERREUR : impossible d'effectuer cette opération, soit un flux XML commençant par '<?xml'... :
#-*- coding: utf-8 -*import RewindableFile
fichier_source = urllib2.urlopen("http://qquepart/")
fichier_source = RewindableFile.RewindableFile(fichier_source)
s = fichier_source.readline()
if s.startswith("ERREUR:"):
raise Exception(s[:-1])
fichier_source.seek(0)
fichier_source.pas_de_tampon() # On ne met plus les données dans le tampon
## ... Traitement du contenu XML de fichier_source ...
La classe de cette recette n’utilise pas un idiome Python parfois utile : vous ne pouvez
pas stocker de façon fiable les méthodes liées d’une instance RewindableFile (si vous
ne savez pas ce qu’est une méthode liée, cela ne pose évidemment pas de problème
car, en ce cas, vous ne souhaiterez sûremement pas les stocker non plus !). La raison
de cette restriction est que lorsque le tampon est vide, le code de RewindableFile
réaffecte les méthodes read, readlines, etc. du fichier d’entrée comme des variables
d’instances de self. Cela améliore légèrement les performances, mais vous ne pouvez
plus sauvegarder les méthodes liées (cela dit, c’est une fonctionnalité rarement utilisée). La recette 5.11 donne un autre exemple d’une technique similaire, où une instance modifie définitivement ses propres méthodes.
88
Chapitre 2 — Fichiers
La méthode tell, qui renvoie la position courante dans un fichier, ne peut être appelée sur une instance de RewindableFile qu’immédiatement après sa création et avant
toute lecture afin d’obtenir la position de début en octets. Son implémentation dans
la classe RewindableFile tente d’obtenir la position réelle dans le fichier enveloppé et
l’utilise comme position de début. Si le fichier enveloppé ne dispose pas de la
méthode tell, l’implémentation de tell dans RewindableFile se contente de renvoyer 0.
Voir aussi
Le site http://www.dalkescientific.com/Python/ contient la dernière version du code de
cette recette ; la documentation sur les objets fichiers et sur le module cStringIO dans
la Library Reference et dans Python en concentré ; la recette 5.11 présente un exemple
d’instance modifiant de façon irréversible son comportement en réaffectant ses
méthodes.
2.15 Adapter un objet apparenté à un fichier
à un véritable objet fichier
Par Michael Kent
Problème
Vous devez passer un objet apparenté à un fichier (le résultat d’un appel à
urllib.urlopen, par exemple) à une fonction ou une méthode qui attend de recevoir
un vrai objet fichier (la fonction marshal.load, par exemple).
Solution
Nous devons écrire toutes les données de l’objet de départ dans un fichier temporaire
sur disque, puis utiliser un vrai objet fichier pour ce fichier temporaire. Voici une
fonction qui utilise cette approche :
import types, os
TAILLE_BLOC = 16 * 1024
def adapter_fichier(objFichier):
if isinstance(objFichier, file): return objFichier
objFichierTmp = os.tmpfile()
while True:
donnees = objFichier.read(TAILLE_BLOC)
if not donnees: break
objFichierTmp.write(donnees)
objFichier.close()
objFichierTmp.seek(0)
return objFichierTmp
Discussion
Cette recette montre une application inhabituelle du motif de conception « Adaptateur » (ce qu’il faut faire lorsque l’on a un X et qu’on a plutôt besoin d’un Y). Bien
que les motifs de conception soient généralement pensés selon une approche orientée
Parcourir des arborescences de répertoires
89
objet et donc implémentés par des classes, rien n’oblige à utiliser ce paradigme. Ici,
par exemple, nous n’avons pas vraiment besoin d’introduire une nouvelle classe car
la fonction adapter_fichier suffit largement. Par conséquent, nous respectons le
principe du rasoir d’Occam en n’introduisant pas d’entités inutiles.
En règle générale, vous devriez raisonner en termes d’adaptation plutôt qu’en termes
de tests de types, même si vous devez utiliser un utilitaire de bas niveau exigeant des
types précis. Au lieu de déclencher une exception lorsque vous recevez un objet totalement approprié, mais n’appartenant pas au type attendu, pensez à la possibilité
d’adapter ce que l’on vous a passé en ce dont vous avez besoin. De cette façon, votre
code sera plus souple et plus facilement réutilisable.
Voir aussi
La documentation sur les objets fichiers prédéfinis et sur les modules tempfile et
marshal dans la Library Reference et dans Python en concentré.
2.16 Parcourir des arborescences de répertoires
Par Robin Parmar et Alex Martelli
Problème
Vous devez examiner un « répertoire » ou toute une arborescence de répertoires
située dans un répertoire donné pour itérer sur les fichiers (et, éventuellement, les
sous-répertoires) dont les noms correspondent à certains motifs.
Solution
Le générateur os.walk du module os de la bibliothèque standard suffit à cette tâche,
mais vous pouvez l’améliorer un peu en écrivant votre propre fonction pour envelopper os.walk :
#-*- coding: utf-8 -*import os, fnmatch
def tous_les_fichiers(racine, motifs = '*', un_seul_niveau = False, repertoires =
False):
# Traduit en liste les motifs séparés par des points-virgules
motifs = motifs.split(';')
for chemin, sous_reps, fichiers in os.walk(racine):
if repertoires:
fichiers.extend(sous_reps)
fichiers.sort()
for nom in fichiers:
for motif in motifs:
if fnmatch.fnmatch(nom, motif):
yield os.path.join(chemin, nom)
break
if un_seul_niveau:
break
90
Chapitre 2 — Fichiers
Discussion
Le générateur standard de parcours d’arborescences de répertoires, os.walk, est puissant, simple et souple. Cependant, il lui manque quelques raffinements pouvant être
utiles aux applications, comme la possibilité de choisir des fichiers correspondant à
des motifs, un parcours linéaire de tous les fichiers (et, éventuellement, des répertoires) dans un ordre donné, ainsi que la possibilité d’examiner un seul répertoire, sans
descendre dans ses sous-répertoires. Cette recette montre comment facilement ajouter
ces fonctionnalités, en enveloppant os.walk dans un autre générateur et en utilisant
le module standard fnmatch pour rechercher les noms de fichiers correspondant à des
motifs.
Les motifs de fichiers peuvent être insensibles à la casse (cela dépend des plates-formes) mais, sinon, ce sont des motifs de style Unix tels qu’ils sont fournis par le
module fnmatch utilisé par cette recette. Pour préciser plusieurs motifs, séparez-les
par des points-virgules : cela signifie donc qu’un motif ne pourra pas contenir de
point-virgule.
La ligne suivante, par exemple, permet d’obtenir la liste de tous les fichiers Python
et HTML contenus dans le répertoire /tmp ou ses sous-répertoires :
fichiers = list(tous_les_fichiers('/tmp', '*.py;*.htm;*.html'))
Si vous souhaitez simplement traiter ces noms de fichiers les uns après les autres (les
afficher chacun sur une ligne, par exemple), vous n’avez pas besoin de construire une
liste : il suffit de boucler sur le résultat de l’appel à tous_les_fichiers :
for chemin in tous_les_fichiers('/tmp', '*.py;*.htm;*.html'):
print chemin
Si votre plate-forme tient compte de la casse dans les noms de fichiers et que vous
souhaitez effectuer des correspondances sensibles à la casse, vous devrez utiliser des
motifs plus complexes : '*.[Hh][Tt][Mm][Ll]' au lieu de '*.html', par exemple.
Voir aussi
La documentation du module os.path et du générateur os.walk, ainsi que celle du
module fnmatch dans la Library Reference et dans Python en concentré.
2.17 Remplacer une extension de fichier par une autre
dans toute une arborescence de répertoires
Par Julius Welby
Problème
Vous devez renommer des fichiers dans toute une arborescence de répertoires, en
modifiant les noms de tous les fichiers ayant une extension donnée pour la remplacer
par une autre.
Remplacer une extension de fichier par une autre dans toute une arborescence de répertoires
91
Solution
Avec la fonction os.walk de la bibliothèque standard de Python, il est assez facile de
travailler sur tous les fichiers d’une arborescence :
import os
def echange_extensions(rep, ancienne, nouvelle):
if ancienne[:1] != '.':
ancienne = '.' + ancienne
longueur = -len(ancienne)
if nouvelle[:1] != '.':
nouvelle = '.' + nouvelle
for chemin, sous_reps, fichiers in os.walk(rep):
for ancien_fichier in fichiers:
if ancien_fichier[longueur:] == ancienne:
ancien_fichier = os.path.join(chemin, ancien_fichier)
nouveau_fichier = ancien_fichier[:longueur] + nouvelle
os.rename(ancien_fichier, nouveau_fichier)
if __name__=='__main__':
import sys
if len(sys.argv) != 4:
print "Usage: echange_extensions racine ancienne nouvelle"
sys.exit(100)
echange_extensions(sys.argv[1], sys.argv[2], sys.argv[3])
Discussion
Cette recette montre comment remplacer les extensions des noms de tous les fichiers
d’un répertoire, de tous ses sous-répertoires, de tous leurs sous-répertoires, etc. Cette
technique est utile pour modifier les extensions d’un lot de fichiers dans une structure
de répertoires comme un site web, mais vous pouvez également l’utiliser pour corriger les erreurs faites lors d’une sauvegarde automatisée d’un ensemble de fichiers.
Cette recette est utilisable à la fois comme module à importer et comme script à lancer à partir de la ligne de commande ; en outre, elle a été soigneusement écrite pour
être indépendante des plates-formes. Vous pouvez passer les extensions en les précédant ou non d’un point car, si nécessaire, la recette l’insérera (cependant, à cause de
cette fonctionnalité, ce code ne peut pas gérer les fichiers n’ayant aucune extension,
même pas le point, ce qui peut être ennuyeux sur les systèmes Unix).
Notre implémentation utilise des techniques que les puristes pourraient considérer
comme étant de trop bas niveau : notamment, elle gère essentiellement les noms de
fichiers et les extensions par des manipulations directes de chaînes plutôt qu’en utilisant les fonctions du module os.path. Ce n’est pas un gros problème : l’utilisation
de os.path comme celle des fonctions puissantes de Python sur les chaînes conviennent toutes les deux.
Voir aussi
La page web de l’auteur, http://www.outwardlynormal.com/python/swapextensions.htm.
92
Chapitre 2 — Fichiers
2.18 Trouver un fichier dans un chemin donné
Par Chui Tey
Problème
Vous voulez retrouver le premier fichier portant un certain nom le long d’un chemin
de recherche (une chaîne de répertoires séparés par un caractère particulier).
Solution
Le travail consiste essentiellement à boucler sur les répertoires du chemin de recherche indiqué :
#-*- coding: utf-8 -*import os
def chercher_fichier(nom_fichier, chemin_rech, sep_chemin = os.pathsep):
""" Recherche le fichier de nom indiqué dans le chemin spécifié."""
for chemin in chemin_rech.split(sep_chemin):
candidat = os.path.join(chemin, nom_fichier)
if os.path.isfile(candidat):
return os.path.abspath(candidat)
return None
if __name__ == '__main__':
chemin_rech = '/bin' + os.pathsep + '/usr/bin' # ; avec Windows, : avec Unix
fichier = chercher_fichier('ls', chemin_rech)
if fichier:
print "Le fichier 'ls' se trouve dans %s." % fichier
else:
print "Le fichier 'ls' est introuvable."
Discussion
Le « problème » de cette recette est une tâche relativement fréquente qui peut être
résolue très facilement en Python. D’autres recettes résolvent des problèmes similaires
et apparentés : la recette 2.19 montre comment trouver tous les fichiers correspondant
à un motif le long d’un chemin de recherche et la recette 2.20 explique comment
rechercher des fichiers le long du propre chemin de recherche de Python.
La boucle de recherche peut être codée de nombreuses façons différentes mais la
méthode la plus simple et la plus rapide consiste à renvoyer le chemin (sous la forme
d’un chemin absolu, pour des raisons d’uniformité et de commodité) dès qu’on l’a
trouvé. L’instruction explicite return None après la boucle n’est pas strictement
nécessaire, car Python renvoie None lorsqu’une fonction atteint la fin de son code.
Cependant, grâce à cette instruction return, la fonctionnalité de rechercher_fichier
apparaît clairement dès la première lecture.
Voir aussi
Les recettes 2.19 et 2.20 ; la documentation du module os dans la Library Reference et
dans Python en concentré.
Chercher dans un chemin les fichiers font les noms correspondent à un certain motif
93
2.19 Chercher dans un chemin les fichiers font
les noms correspondent à un certain motif
Par Bill McNeill et Andrew Kirkpatrick
Problème
Vous devez rechercher tous les fichiers dont les noms correspondent à un certain
motif le long d’un chemin de recherche (une chaîne de répertoires séparés par un
caractère particulier).
Solution
La solution consiste essentiellement à boucler sur les répertoires du chemin de
recherche. Il est préférable d’encapsuler cette boucle dans un générateur :
#-*- coding: utf-8 -*import glob, os
def tous_les_fichiers(motif, chemin_rech, sep_chemin = os.pathsep):
""" Produit tous les fichiers correspondant au motif dans chemin_rech."""
for chemin in chemin_rech.split(sep_chemin):
for corresp in glob.glob(os.path.join(chemin, motif)):
yield corresp
Discussion
L’un des avantages des générateurs est que vous pouvez aisément les utiliser pour
n’obtenir que le premier élément, tous les éléments ou un nombre quelconque d’éléments. Le code suivant, par exemple, affichera le premier fichier correspondant à
'*.pye' dans votre PATH :
print tous_les_fichiers('*.pye', os.environ['PATH']).next()
Pour afficher tous ces fichiers, ligne par ligne :
for corresp in tous_les_fichiers('*.pye', os.environ['PATH']):
print corresp
Pour les afficher tous, sous la forme d’une liste :
print list(tous_les_fichiers('*.pye', os.environ['PATH']))
J’utilise également la fonction tous_les_fichiers dans un script principal, afin d’afficher tous les fichiers de mon PATH correspondant à un certain motif. De cette façon,
je peux savoir non seulement celui qui sera exécuté si j’appelle ce nom (le premier),
mais également ceux qui sont « masqués » par le premier :
if __name__ == '__main__':
import sys
if len(sys.argv) != 2 or sys.argv[1].startswith('-'):
print 'Usage: %s <motif>' % sys.argv[0]
sys.exit(1)
corresps = list(tous_les_fichiers(sys.argv[1], os.environ['PATH']))
print '%d correspondances :' % len(corresps)
for corresp in corresps:
print corresp
94
Chapitre 2 — Fichiers
Voir aussi
La recette 2.18 décrit une approche plus simple pour trouver le premier fichier portant un certain nom le long d’un chemin ; les documentations des modules os et glob
dans la Library Reference et dans Python en concentré.
2.20 Rechercher un fichier dans le chemin
de recherche de Python
Par Mitch Chapman
Problème
Une grosse application Python contient des fichiers ressources (des fichiers de projet
Glade, des patrons SQL et des images, par exemple) ainsi que des paquetages Python.
Vous voulez stocker ces fichiers avec les paquetages Python qui les utilisent.
Solution
Il faut rechercher les fichiers ou les répertoires dans le sys.path de Python :
import sys, os
class Erreur(Exception): pass
def _chercher(chemin, fction_corresp = os.path.isfile):
for rep in sys.path:
candidat = os.path.join(rep, chemin)
if fction_corresp(candidat):
return candidat
raise Erreur("Le fichier %s est introuvable." % chemin)
def chercherFichier(chemin):
return _chercher(chemin)
def chercherRepertoire(chemin):
return _chercher(chemin, fction_corresp = os.path.isdir)
Discussion
Les plus grosses applications Python sont formées de paquetages Python et de fichiers
de ressources associés. Il est commode de stocker ces fichiers associés avec les paquetages Python qui les utilisent : en utilisant cette variante de la recette 2.18, il est facile
de rechercher les fichiers ou les répertoires dont les noms sont relatifs au chemin de
recherche de Python.
Voir aussi
La recette 2.18 ; la documentation du module os dans la Library Reference et dans
Python en concentré.
Modifier dynamiquement le chemin de recherche de Python
95
2.21 Modifier dynamiquement le chemin de recherche
de Python
Par Robin Parmar
Problème
Pour être importés, les modules doivent se trouver sur le chemin de recherche de
Python, mais vous ne souhaitez pas configurer en permanence un chemin très long
car cela ralentit l’exécution — vous voulez modifier ce chemin dynamiquement.
Solution
On ajoute simplement un « répertoire » au sys.path de Python, en prenant soin
d’éviter les doublons :
#-*- coding: utf-8 -*def AjoutSysPath(nouveau_chemin):
""" AjoutSysPath(nouveau_chemin) : ajoute un répertoire au sys.path de
Python. Le répertoire n'est pas ajouté s'il n'existe pas ou s'il est
déjà dans le sys.path. Renvoie 1 si OK, -1 si nouveau_chemin n'existe
pas, 0 s'il était déjà dans sys.path.
"""
import sys, os
# Évite les chemins inexistants
if not os.path.exists(nouveau_chemin): return -1
# Standardise le chemin. Windows ne tient pas compte de la casse, on le
# met donc en minuscules si l'on est sous Windows.
nouveau_chemin = os.path.abspath(nouveau_chemin)
if sys.platform == 'win32':
new_path = nouveau_chemin.lower()
# Vérifie s'il est déjà dans sys.path
for x in sys.path:
x = os.path.abspath(x)
if sys.platform == 'win32':
x = x.lower()
if nouveau_chemin in (x, x + os.sep):
return 0
sys.path.append(nouveau_chemin)
# Si on veut que nouveau_chemin ait priorité sur les répertoires déjà
# dans sys.path, il suffit de faire sys.path.insert(0, nouveau_chemin) au
# lieu de l'ajouter à la fin.
return 1
if __name__ == '__main__':
# Teste et affiche la syntaxe
import sys
print 'Avant :'
for x in sys.path: print x
if sys.platform == 'win32':
print AjoutSysPath('c:\\Temp')
print AjoutSysPath('c:\\temp')
96
Chapitre 2 — Fichiers
else:
print AjoutSysPath('/usr/lib/mes_modules')
print 'Après :'
for x in sys.path: print x
Discussion
Les modules doivent se trouver dans des répertoires du chemin de recherche de
Python avant d’être importés, mais utiliser un énorme chemin permanent ralentit
chaque opération d’importation réalisée par les scripts ou les applications. Cette
recette simple ajoute dynamiquement un « répertoire » au chemin, mais uniquement
si ce répertoire existe et s’il n’est pas déjà dans sys.path.
sys.path est une liste, il est donc facile d’y ajouter des répertoires à la fin avec
sys.path.append. Tout import effectué après cet append recherchera automatiquement le module dans le nouveau répertoire ajouté si les précédents ne lui ont pas
donné satisfaction. Comme l’indique la solution, vous pouvez également utiliser
sys.path.insert(0, ...) pour que le nouveau répertoire soit parcouru avant ceux
qui se trouvaient déjà dans sys.path.
Si sys.path contient des doublons ou qu’un répertoire inexistant lui est ajouté
accidentellement, cela ne pose pas de problème : l’instruction import de Python est
suffisamment maligne pour se protéger de telles situations. Cependant, à chaque fois
qu’un tel problème survient lors d’une importation (recherches dupliquées infructueuses, erreurs du système d’exploitation devant être gérées correctement, etc.) cela
se paye en termes de performances. Pour éviter cette dépense inutile, notre recette
n’ajoute jamais un répertoire inexistant ou déjà présent dans sys.path. Les répertoires ajoutés par cette recette ne restent dans sys.path que le temps de l’exécution du
programme, comme toute autre modification dynamique que vous pourriez apporter
à ce chemin.
Voir aussi
La documentation des modules sys et os.path dans la Library Reference et dans Python
en concentré.
2.22 Calculer le chemin relatif d’un répertoire
par rapport à un autre
Par Cimarron Taylor et Alan Ezust
Problème
Vous avez besoin de connaître le chemin relatif d’un répertoire par rapport à un
autre — pour créer un lien symbolique ou une référence relative dans une URL, par
exemple.
Solution
L’approche la plus simple consiste à découper les chemins en listes de répertoires, puis
à travailler sur ces listes. À l’aide de deux fonctions auxiliaires relativement génériques, nous pouvons écrire le code suivant :
Calculer le chemin relatif d’un répertoire par rapport à un autre
97
#-*- coding: utf-8 -*import os, itertools
def tous_egaux(elements):
''' Renvoie True si tous les éléments sont égaux, False sinon.'''
premier_element = elements[0]
for autre_element in elements[1:]:
if autre_element != premier_element: return False
return True
def prefixes_communs(*sequences):
''' Renvoie une liste des éléments communs au début de chaque séquence,
puis une liste de listes formées des fins uniques de chaque
séquence. '''
# Aucune séquence, c'est fini
if not sequences: return [], []
# Boucle en parallèle sur les séquences
communs = []
for elements in itertools.izip(*sequences):
# Si tous les éléments ne sont pas égaux, on sort de la boucle
if not tous_egaux(elements): break
# On a un élément commun de plus, on l'ajoute et on continue la boucle
communs.append(elements[0])
# Renvoie les préfixes communs et les fins uniques
return communs, [ sequence[len(communs):] for sequence in sequences ]
def chemin_relatif(p1, p2, sep = os.path.sep, rep_parent = os.path.pardir):
''' Renvoie le chemin relatif de p2 par rapport à p1
Notamment la chaîne vide si p1 == p2 ;
p2, si p1 et p2 n'ont pas de préfixe commun.
'''
communs, (u1, u2) = prefixes_communs(p1.split(sep), p2.split(sep))
if not communs:
return p2
# Laisse le chemin absolu s'ils n'ont rien en commun
return sep.join( [rep_parent]*len(u1) + u2 )
def test(p1, p2, sep = os.path.sep):
''' Appelle la fonction chemin_relatif et affiche les résultats.'''
print "De", p1, "à", p2, " -> ", chemin_relatif(p1, p2, sep)
if __name__ == '__main__':
test('/a/b/c/d', '/a/b/c1/d1', '/')
test('/a/b/c/d', '/a/b/c/d', '/')
test('c:/x/y/z', 'd:/x/y/z', '/')
Discussion
L’essentiel du travail de cette recette est effectué par la fonction prefixes_communs,
simple mais très générale, qui, à partir de N séquences, renvoie leurs préfixes communs et une liste de leurs fins uniques respectives. Pour calculer le chemin relatif
entre deux chemins, nous pouvons ignorer leurs préfixes communs car nous n’avons
besoin que du nombre approprié de marqueurs de remontée (généralement
os.path.pardir — ../ sur les systèmes Unix ; il en faut autant que la longueur de la
fin unique du répertoire de départ) suivi de la fin unique du répertoire de destination.
La fonction chemin_relatif coupe donc les chemins en listes de répertoires, appelle
prefixes_communs, puis effectue la construction que nous venons de décrire.
98
Chapitre 2 — Fichiers
prefixe_communs
s’articule
sur
la
boucle
for
elements
in
itertools.izip(*sequences), en exploitant le fait que izip se termine avec le plus
court des itérables qu’elle réunit. Le corps de la boucle a simplement besoin de terminer la boucle dès qu’il rencontre un tuple d’éléments (provenant chacun de chaque
séquence d’après les spécifications de izip) qui ne sont pas tous égaux et de
mémoriser les éléments qui le sont en ajoutant l’un d’entre eux à la fin de la liste
communs. À la fin de la boucle, il reste à préparer les listes à renvoyer en supprimant
les éléments déjà dans communs au début de chacune des séquences.
La fonction tous_egaux peut également être écrite d’une façon totalement différente.
Cele donnera un code moins simple et moins évident, mais intéressant :
def tous_egaux(elements):
return len(dict.fromkeys(elements)) == 1
ou, en Python 2.4 seulement, de manière équivalente mais plus concise :
def tous_egaux(elements):
return len(set(elements)) == 1
Dire que tous les éléments sont égaux revient à dire que l’ensemble (set) des éléments
a une cardinalité (un nombre d’éléments) de un. Dans la variante utilisant
dict.fromkeys, nous utilisons un dict pour représenter l’ensemble : elle fonctionne
donc avec Python 2.3 comme avec Python 2.4. La variante utilisant set est plus claire,
mais elle ne fonctionne qu’avec Python 2.4 (vous pouvez également la faire fonctionner avec Python 2.3 et 2.4 en utilisant le module sets de la bibliothèque standard).
Voir aussi
La documentation des modules os et itertools dans la Library Reference et dans
Python en concentré.
2.23 Lire un caractère non tamponné
de façon portable
Par Danny Yoo
Problème
Votre application doit lire des caractères simples et non tamponnés sur l’entrée standard et elle doit fonctionner sur des systèmes Windows comme sur des systèmes Unix.
Solution
Lorsque l’on a besoin d’une solution multi-plateforme et que l’on dispose de solutions
spécifiques, il suffit de les envelopper pour pour donner l’illusion d’une solution unique :
try:
from msvcrt import getch
except ImportError:
''' On n'est pas sous Windows, on essaie donc l'approche Unix.'''
Lire un caractère non tamponné de façon portable
99
def getch():
import sys, tty, termios
fd = sys.stdin.fileno()
ancienne_config = termios.tcgetattr(fd)
try:
tty.setraw(fd)
car = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, ancienne_config)
return car
Discussion
Avec Windows, le module msvcrt de la bibliothèque standard fournit la fonction
getch pour lire au clavier un caractère unique, non tamponné, sans l’afficher à
l’écran. Cependant, ce module ne fait pas partie de la bibliothèque standard de
Python sur les plates-formes Unix et apparentées, comme Linux et Mac OS X. Sur
celles-ci, les modules tty et termios (qui, inversement, ne sont pas disponibles sur
Windows), permettent d’obtenir la même fonctionnalité.
Le point essentiel est que au niveau du code de l’application, nous ne devrions jamais
avoir à nous soucier de ces problèmes. Nous devrions écrire notre application de
façon portable, indépendamment des plates-formes, en faisant confiance aux fonctions
de la bibliothèque pour gommer les différences entre les systèmes. La bibliothèque
standard de Python remplit admirablement ce rôle dans la plupart des cas, mais pas
toujours ; le problème posé par cette recette est un exemple de situation où cette
bibliothèque ne fournit pas directement de solution portable.
Si nous ne pouvons pas trouver de solution portable toute prête dans la librairie standard, nous devrions en écrire une et l’intégrer sous forme de paquetage à notre
bibliothèque personnelle. La solution de cette recette, outre qu’elle résout le problème initial, montre également une méthode générale pour créer ce genre de paquetage (vous pouvez également tester sys.platform, mais je préfère ma solution).
Notre module de bibliothèque devrait utiliser une clause try pour tenter d’importer
le module standard dont il a besoin sur une plate-forme donnée et une clause except
ImportError qui sera déclenchée lorsqu’il s’exécute sur une plate-forme différente.
Dans le corps de cette clause except, notre module peut alors utiliser l’approche qui
fonctionnera sur l’autre plate-forme. Dans quelques cas rares, vous pouvez avoir
besoin de prendre en compte plus de deux plates-formes mais, le plus souvent, vous
n’aurez besoin que d’une approche pour Windows et d’une autre pour tous les autres
systèmes. En effet, la plupart des plates-formes non Windows actuelles sont généralement des systèmes Unix ou apparentés.
Voir aussi
La documentation de msvcrt, tty et termios dans la Library Reference et dans Python
en concentré.
100
Chapitre 2 — Fichiers
2.24 Compter les pages d’un document PDF
sur Mac OS X
Par Dinu Gherman et Dan Wolfe
Problème
Vous utilisez une version assez récente de Mac OS X (version 10.3 « Panther » ou
supérieure) et vous voulez connaître le nombre de pages d’un document PDF.
Solution
Le format PDF et Python sont directement intégrés à Mac OS X (à partir de sa version
10.3), ce qui simplifie beaucoup la solution :
#!/usr/bin python
import CoreGraphics
def pageCount(cheminPdf):
"""Renvoie le nombre de page du document PDF cheminPdf."""
pdf = CoreGraphics.CGPDFDocumentCreateWithProvider(
CoreGraphics.CGDataProviderCreateWithFilename(cheminPdf)
)
return pdf.getNumberOfPages()
if __name__ == '__main__':
import sys
for chemin in sys.argv[1:]:
print pageCount(chemin)
Discussion
Une alternative possible à cette recette pourrait consister à utiliser l’extension PyObjC
qui (entre autres choses) permet à Python de réutiliser toute la puissance des
frameworks Foundation et AppKit fournis par Mac OS X. Cette approche vous permettrait d’écrire un script Python pouvant également s’exécuter sur des versions plus
anciennes de ce système d’exploitation, comme sa version 10.2 « Jaguar ». Cependant,
exiger une version 10.3 ou ultérieure de Mac OS X 10.3 garantit que nous pouvons
utiliser l’installation de Python intégrée au système, ainsi que des gâteries comme le
module d’extension Python CoreGraphics (faisant également partie de Mac OS X
« Panther »), qui permet à votre code Python de réutiliser directement Quartz,
l’excellent moteur graphique d’Apple.
Voir aussi
Le site consacré à PyObjC est http://pyobjc.sourceforge.net/ ; pour plus d’informations sur
le module CoreGraphics, consultez la page http://www.macdevcenter.com/pub/a/mac/
2004/03/19/core_graphics.html.
Modifier les attributs de fichiers sur Windows
101
2.25 Modifier les attributs de fichiers sur Windows
Par John Nielsen
Problème
Vous devez configurer les attributs d’un fichier sous Windows ; vous souhaitez, par
exemple, le mettre en lecture seule, en mode archivé, etc.
Solution
Grâce au module win32api de PyWin32, qui fournit une fonction SetFileAttributes,
la solution est assez simple :
#-*- coding: utf-8 -*import win32con, win32api, os
# On crée un fichier, juste pour montrer comment le manipuler
fichier = 'test'
f = open('test', 'w')
f.close()
# On cache le fichier :
win32api.SetFileAttributes(fichier, win32con.FILE_ATTRIBUTE_HIDDEN)
# On le met en lecture seule :
win32api.SetFileAttributes(fichier, win32con.FILE_ATTRIBUTE_READONLY)
# Pour pouvoir supprimer le fichier, il faut le remettre en mode normal :
win32api.SetFileAttributes(fichier, win32con.FILE_ATTRIBUTE_NORMAL)
# Enfin, on supprime le fichier que l'on a créé.
os.remove(fichier)
Discussion
Une utilisation intéressante de win32api.SetFileAttributes est qu’elle permet
d’autoriser la suppression d’un fichier. En effet, os.remove peut échouer sous
Windows si les attributs du fichiers ne sont pas en mode normal. Pour contourner ce
problème, il suffit donc d’utiliser l’appel Win32 SetFileAttributes pour le convertir
en fichier normal, comme on le fait à la fin de la solution de cette recette. Il faut, bien
sûr, être prudent lorsque l’on fait cette manœuvre car il y a peut-être une bonne raison pour laquelle le fichier n’est pas « normal ». Le fichier ne doit être supprimé que
si vous savez ce que vous faites !
Voir aussi
La documentation sur le module win32file, disponible à l’URL http://aspn.
activestate.com/ASPN/docs/ActivePython/2.3/pywin32/win32file.html
ou
http://aspn.
activestate.com/ASPN/docs/ActivePython/2.4/pywin32/win32file.html suivant la version de
Python que vous utilisez.
102
Chapitre 2 — Fichiers
2.26 Extraire du texte d’un document OpenOffice.org
Par Dirk Holtwick
Problème
Vous devez extraire le contenu textuel (avec ou sans le marquage XML) d’un document OpenOffice.org.
Solution
Un document OpenOffice.org est simplement un fichier zip rassemblant des fichiers
XML respectant un standard bien défini. Pour accéder à nos précieuses données, nous
n’avons même pas besoin que OpenOffice.org soit installé :
#-*- coding: utf-8 -*import zipfile, re
re_suppr_xml = re.compile("<[^>]*?>", re.DOTALL|re.MULTILINE)
def convertir_OO(nomfichier, texte_seul = True):
""" Convertit un document OpenOffice.org en XML ou en texte. """
fz = zipfile.ZipFile(nomfichier, "r")
donnees = fz.read("content.xml")
fz.close()
if texte_seul:
donnees = " ".join(re_suppr_xml.sub(" ", donnees).split())
return donnees
if __name__=="__main__":
import sys
if len(sys.argv)>1:
for nomdoc in sys.argv[1:]:
print 'Texte de', nomdoc, ':'
print convertir_OO(nomdoc)
print 'XML de', nomdoc, ':'
print convertir_OO(nomdoc, texte_seul = False)
else:
print 'Donnez des noms de documents OO.o pour les lire aux formats texte
et XML.'
Discussion
Les documents OpenOffice.org sont des fichiers zip qui contiennent toujours un fichier
content.xml en plus d’autres contenus. Le travail de cette recette, par conséquent, se
ramène essentiellement à extraire ce fichier. Par défaut, la recette supprime les balises
XML à l’aide d’une expression régulière, découpe le résultat selon les espaces et le
réunit à nouveau avec un espace unique pour économiser la place occupée. Vous
pourriez, bien sûr, utiliser un parseur XML pour obtenir les informations de façon
bien plus riche et structurée mais, si nous ne nous intéressons qu’au contenu textuel,
cette approche rapide et toute prête peut suffire.
L’expression régulière re_suppr_xml capture n’importe quelle balise XML (ouvrante
ou fermante) comprise entre < et >. À l’intérieur de la fonction convertir_OO, dans
les instructions conditionnées par if texte_seul, nous utilisons cette expression
régulière pour remplacer chaque balise XML par un espace, puis nous normalisons les
Extraire le texte d’un document Word de Microsoft
103
espaces en découpant (c’est-à-dire en appelant la méthode split des chaînes, qui
divise un texte sur toute suite d’espaces) et en réunissant (avec " ".join, pour utiliser
un seul caractère espace comme jointure). Ce traitement de division et de réunion
revient donc à remplacer toute suite d’espaces par un seul. Vous trouverez des
méthodes plus élaborées pour extraire tout le texte d’un document XML dans la
recette 8.3.
Voir aussi
La documentation des modules zipfile et re dans la Library Reference ; le site web de
OpenOffice.org, http://www.openoffice.org/ ; la recette 8.3.
2.27 Extraire le texte d’un document Word
de Microsoft
Par Simon Brunning et Pavel Kosina
Problème
Sous Windows, vous voulez extraire le contenu textuel de tous les documents Word
d’une arborescence dans des fichiers textes correspondants.
Solution
Grâce à l’extension PyWin32, nous pouvons accéder à Word via COM pour effectuer
cette conversion :
#-*- coding: utf-8 -*import fnmatch, os, sys, win32com.client
wordapp = win32com.client.gencache.EnsureDispatch("Word.Application")
try:
for chemin, reps, fichiers in os.walk(sys.argv[1]):
for nomfic in fichiers:
if not fnmatch.fnmatch(nomfic, '*.doc'): continue
doc = os.path.abspath(os.path.join(chemin, nomfic))
print "Traitement de %s" % doc
wordapp.Documents.Open(doc)
doc_en_txt = doc[:-3] + 'txt'
wordapp.ActiveDocument.SaveAs(doc_en_txt,
FileFormat = win32com.client.constants.wdFormatText)
wordapp.ActiveDocument.Close()
finally:
# Garantit que Word est correctement fermé, même si on reçoit une exception
wordapp.Quit()
Discussion
Un aspect pratique de la plupart des applications Windows est que vous pouvez les
scripter avec COM ; grâce à l’extension PyWin32 c’est chose facile à partir de Python
car cette extension permet d’écrire des scripts Python capables d’effectuer différents
types de tâches Windows. Celui de cette recette pilote Microsoft Word pour extraire
104
Chapitre 2 — Fichiers
le texte de chaque fichier .doc d’une arborescence de répertoires dans des fichiers .txt
correspondants. Grâce à la fonction os.walk, nous pouvons accéder à chaque sousrépertoire d’une arborescence à l’aide d’une simple instruction for, sans avoir besoin
de la récursivité. La fonction fnmatch.fnmatch permet de vérifier qu’un nom de
fichier correspond à un joker particulier, ici '*.doc'. Lorsque l’on a déterminé le
nom d’un document Word, on le traite avec les fonctions de os.path pour le transformer en chemin absolu et on demande à Word de l’ouvrir, de le sauvegarder au
format texte et de le refermer.
Si vous ne possédez pas Word, vous devrez utiliser une approche totalement différente. Une possibilité consiste à utiliser OpenOffice.org, qui est capable d’ouvrir des
documents Word. Une autre est d’utiliser un programme spécialement conçu pour
lire les documents Word, comme Antiword, que vous trouverez à http://
www.winfield.demon.nl/. Cependant, nous n’avons pas exploré ces possibilités.
Voir aussi
Python Programming on Win32 de Mark Hammond et Andy Robinson (O’Reilly), pour
la documentation de PyWin32 ; http://msdn.microsoft.com pour la documentation de
Microsoft sur le modèle objet de Word ; les sections consacrées aux modules fnmatch
et os.path, ainsi qu’à la fonction os.walk dans la Library Reference et dans Python en
concentré.
2.28 Verrouiller un fichier avec une API portable
Par Jonathan Feinberg et John Nielsen
Problème
Vous devez verrouiller des fichiers dans un programme s’exécutant à la fois sous
Windows et sur des systèmes Unix, mais la bibliothèque standard de Python n’offre
que des solutions spécifiques pour cette tâche.
Solution
Lorsque la bibliothèque standard de Python n’offre pas de solution portable, il est
souvent possible d’en implémenter une par soi-même :
#-*- coding: utf-8 -*import os
# Nécessite win32all pour fonctionner avec Windows (NT, 2K, XP, _ni_ /95 ni /98)
if os.name == 'nt':
import win32con, win32file, pywintypes
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
LOCK_SH = 0 # Valeur par défaut
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
__overlapped = pywintypes.OVERLAPPED()
def lock(fichier, options):
hfile = win32file._get_osfhandle(fichier.fileno())
win32file.LockFileEx(hfile, options, 0, 0xffff0000, __overlapped)
def unlock(fichier):
hfile = win32file._get_osfhandle(fichier.fileno())
Verrouiller un fichier avec une API portable
105
win32file.UnlockFileEx(hfile, 0, 0xffff0000, __overlapped)
elif os.name == 'posix':
from fcntl import LOCK_EX, LOCK_SH, LOCK_NB
def lock(fichier, options):
fcntl.flock(fichier.fileno(), options)
def unlock(fichier):
fcntl.flock(fichier.fileno(), fcntl.LOCK_UN)
else:
raise RuntimeError("PortaLocker ne fonctionne que sur NT ou les systèmes
POSIX.")
Discussion
Lorsque plusieurs programmes ou plusieurs threads doivent accéder à un fichier partagé, il est prudent de s’assurer que ces accès seront synchronisés afin que deux processus ne puissent pas modifier ce fichier en même temps. Dans certains cas, une
absence de synchronisation pourrait même corrompre tout le fichier.
Cette recette fournit deux fonctions, lock et unlock, permettant respectivement de
demander et de relâcher des verrous sur les fichiers. L’utilisation du module
portalocker.py consiste simplement à appeler la fonction lock en lui passant le nom
du fichier ainsi qu’un paramètre précisant le type de verrou désiré :
Verrou partagé (par défaut)
Ce verrou interdit à tous les processus d’écrire dans le fichier, y compris à celui qui
l’a verrouillé en premier. Tous les processus, par contre, peuvent lire le fichier verrouillé.
Verrou exclusif
Ce verrou interdit à tous les autres processus de lire ou d’écrire dans le fichier.
Verrou non bloquant
Lorsqu’un verrou est non bloquant, la fonction se termine immédiatement si elle
n’a pas pu obtenir le verrou demandé. Sinon, elle attend. LOCK_NB peut être combiné avec LOCK_SH ou LOCK_EX en utilisant l’opérateur OU bit à bit de Python,
c’est-à-dire la barre verticale (|).
Voici un exemple :
import portalocker
fichier = open("un_fichier", "r+")
portalocker.lock(fichier, portalocker.LOCK_EX)
Les implémentations des fonctions lock et unlock sont totalement différentes selon
les systèmes. Sur les plates-formes Unix (comme Linux et Mac OS X), la recette utilise
la fonctionnalité fournie par le module standard fcntl. Sur les systèmes Windows
(NT, 2000, XP — cela ne fonctionne pas sur les anciennes plates-formes Win/95 et
Win/98 parce qu’elle ne disposent tout simplement pas des éléments nécessaires !), la
recette utilise la module win32file qui fait partie du paquetage PyWin32 bien connu,
conçu par Mark Hammond. Cependant, le point important est que les fonctions (et
les options passées à la fonction lock) ont été conçues pour se comporter de la même
façon sur toutes les plates-formes, malgré les différences d’implémentation. La possibilité d’écrire des paquetages de fonctionnalités équivalentes mais implémentées
106
Chapitre 2 — Fichiers
différemment permet d’écrire aisément des applications portables, ce qui est l’une des
grandes forces de Python.
Lorsque vous écrivez un programme portable, il est souhaitable que la fonctionnalité
qu’il utilise soit elle-même encapsulée de façon portable. Pour le verrouillage de
fichiers, notamment, cette précaution est très utile pour les utilisateurs de Perl, qui
sont habitués à un appel système lock transparent quelle que soit la plate-forme qu’ils
utilisent. Plus généralement, if os.name== ne doit pas apparaître dans le code d’une
application : ce type de test devrait toujours avoir lieu dans la bibliothèque standard
ou dans un module indépendant de l’application, comme nous l’avons fait ici.
Voir aussi
La documentation sur le module fcntl dans la Library Reference ; la documentation
sur le module win32file, http://aspn.activestate.com/ASPN/docs/ActivePython/2.3/pywin32/
win32file.html ou http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/win32file.
html suivant la version de Python que vous utilisez ; le site web de Jonathan Feinberg
(http://MrFeinberg.com).
2.29 Ajouter des numéros de versions à des noms
de fichiers
Par Robin Parmar et Martin Miller
Problème
Vous voulez faire une copie de sauvegarde d’un fichier avant de le modifier, en utilisant la convention standard consistant à ajouter un numéro de version de trois chiffres à la fin du nom de l’ancien fichier.
Solution
Il suffit de coder une fonction pour effectuer la sauvegarde de façon appropriée :
#-*- coding: utf-8 -*def VersionFile(spec_fichier, type_vers = 'copier'):
import os, shutil
if os.path.isfile(spec_fichier):
# Vérification du paramètre 'type_vers'
if type_vers not in ('copier', 'renommer'):
raise ValueError, 'Le type_vers %r est inconnu' % (type_vers,)
# Détermine la racine du nom du fichier pour se débarrasser de l'extension
n, e = os.path.splitext(spec_fichier)
# Est-ce que e est un nombre de 3 chiffres précédé d'un point ?
if len(e) == 4 and e[1:].isdigit():
num = 1 + int(e[1:])
racine = n
else:
num = 0
racine = spec_fichier
# Trouve la version disponible suivante du fichier
Ajouter des numéros de versions à des noms de fichiers
107
for i in xrange(num, 1000):
nouveau_fichier = '%s.%03d' % (racine, i)
if not os.path.exists(nouveau_fichier):
if type_vers == 'copier':
shutil.copy(spec_fichier, nouveau_fichier)
else:
os.rename(spec_fichier, nouveau_fichier)
return True
raise RuntimeError, "Impossible de %s %r, tous les noms sont pris" %
(type_vers, spec_fichier)
return False
if __name__ == '__main__':
import os
# Crée un fichier 'test.txt' bidon
tfn = 'test.txt'
open(tfn, 'w').close()
# Crée 3 versions
print VersionFile(tfn)
# Affiche : True
print VersionFile(tfn)
# Affiche : True
print VersionFile(tfn)
# Affiche : True
# Supprime tous les test.txt* venant d'être créés
for x in ('', '.000', '.001', '.002'):
os.unlink(tfn + x)
# Montre ce qui se passe si le fichier n'existe pas
print VersionFile(tfn)
# Affiche : False
print VersionFile(tfn)
# Affiche : False
Discussion
Le but de la fonction VersionFile est de garantir qu’un fichier existant sera copié (ou
renommé, selon ce qu’indique le second paramètre facultatif) avant que vous ne
l’ouvriez pour le modifier. Il est prudent de faire ce type de sauvegarde avant de
modifier un fichier (certains regrettent encore cette fonctionnalité du bon vieux système d’exploitation VMS, qui le faisait automatiquement !). La véritable copie ou le
renommage sont effectués, respectivement, par shutil.copy et os.rename ; le seul
problème à résoudre est le nom à utiliser pour la cible.
Une méthode courante pour déterminer les noms des sauvegardes consiste à ajouter
des numéros de versions à la fin des noms de fichiers et à incrémenter successivement
ces numéros. Cette recette détermine le nouveau nom en commençant par extraire
la racine du nom du fichier (au cas où vous appelleriez la fonction sur un nom de
fichier contenant déjà un numéro de version), puis lui ajoute successivement les
extensions .000, .001, etc. jusqu’à ce qu’un nom construit de cette façon ne corresponde à aucun fichier existant. Alors, et seulement alors, ce nom sera utilisé comme
cible de la copie ou du renommage. Vous remarquerez que VersionFile est limité à
1000 versions : si vous atteignez cette limite, vous devrez avoir mis au point un plan
d’archivage. Le fichier doit exister avant que sa première version ne soit créée — vous
108
Chapitre 2 — Fichiers
ne pouvez pas sauvegarder ce qui n’existe pas encore. Cependant, si le fichier n’existe
pas, la fonction VersionFile renverra simplement False (alors qu’elle renvoie True
si le fichier existe et a pu recevoir un numéro de version).
Voir aussi
La documentation des modules os et shutil dans la Library Reference et dans Python
en concentré.
2.30 Calculer une somme de contrôle CRC-64
Par Gian Paolo Ciceri
Problème
Vous souhaitez garantir l’intégrité de certaines données en calculant leur CRC et en
respectant les spécifications CRC-64 de la norme ISO-3309.
Solution
Comme la bibliothèque standard de Python ne propose pas d’implémentation de
CRC-64 (mais une de CRC-32 dans la fonction zlib.crc32), nous devons la programmer nous-même. Heureusement, Python peut effectuer les mêmes opérations bit à bit
(masquage, décalage, et bit à bit, ou bit à bit, ou exclusif, etc.) que le langage C (en
fait, il utilise exactement la même syntaxe) : il est donc facile de traduire une
implémentation de référence de CRC-64 en une fonction Python :
#-*- coding: utf-8 -*# Prépare deux tableaux auxiliaires (à l'aide d'une fonction pour aller plus
# vite) puis supprime la fonction puisque l'on n'en a plus besoin :
CRCTableh = [0] * 256
CRCTablel = [0] * 256
def _inittables(CRCTableh, CRCTablel, POLY64REVh, BIT_TOGGLE):
for i in xrange(256):
partl = i
parth = 0L
for j in xrange(8):
rflag = partl & 1L
partl >>= 1L
if parth & 1:
partl ^= BIT_TOGGLE
parth >>= 1L
if rflag:
parth ^= POLY64REVh
CRCTableh[i] = parth
CRCTablel[i] = partl
# Premiers 32 bits du polynôme générateur de CRC64 (les 32 bits de poids faible
# sont supposés être à zéro) et masque d'inversion de bit utilisé par _inittables
POLY64REVh = 0xd8000000L
BIT_TOGGLE = 1L << 31L
# Lance la fonction pour préparer les tableaux
_inittables(CRCTableh, CRCTablel, POLY64REVh, BIT_TOGGLE)
Calculer une somme de contrôle CRC-64
109
# Supprime tous les noms dont on n'a plus besoin, dont la fonction
del _inittables, POLY64REVh, BIT_TOGGLE
# Ce module expose deux fonctions : crc64 et crc64digest
def crc64(octets, (crch, crcl) = (0,0)):
for octet in octets:
shr = (crch & 0xFF) << 24
temp1h = crch >> 8L
temp1l = (crcl >> 8L) | shr
indice = (crcl ^ ord(octet)) & 0xFF
crch = temp1h ^ CRCTableh[indice]
crcl = temp1l ^ CRCTablel[indice]
return crch, crcl
def crc64digest(chaine):
return "%08X%08X" % (crc64(chaine))
if __name__ == '__main__':
# Petit test exécuté lorsque ce module est utilisé comme un script
assert crc64("IHATEMATH") == (3822890454, 2600578513)
assert crc64digest("IHATEMATH") == "E3DCADD69B01ADD1"
print 'crc64 : succès du test'
Discussion
Les sommes de contrôle cycliques (CRC, pour Cyclic redundancy check) sont fréquemment employées pour vérifier que des données (un fichier, notamment) n’ont pas été
endommagées accidentellement. Les CRC permettent de détecter facilement ce type
de dommage mais, contrairement à d’autres sommes de contrôle cryptographiques, ils
ne sont pas conçus pour résister à une attaque hostile. Ils peuvent cependant être calculés bien plus rapidement que les autres types de sommes de contrôle, ce qui les rend
intéressants dans les cas où le seul dommage contre lequel ont souhaite se prémunir
est de type accidentel et non délibéré.
Mathématiquement parlant, un CRC est calculé comme un polynôme sur les bits des
données contrôlées. En pratique, comme le montre la recette, l’essentiel du calcul
peut être effectué une fois pour toute et résumé dans des tableaux qui, s’ils sont correctement indicés, donnent la contribution de chaque octet des données d’entrée au
résultat. Par conséquent, après l’initialisation (que nous réalisons à l’aide d’une fonction auxiliaire car le calcul en Python est bien plus rapide lorsque l’on utilise les
variables locales d’une fonction plutôt que des variables globales), le calcul du CRC
proprement dit est assez rapide. Le calcul des tableaux et leur utilisation pour le calcul du CRC nécessite un grand nombre d’opérations bit à bit mais, heureusement,
Python est, dans ce domaine, aussi bon que des langages comme C.
L’algorithme utilisé pour calculer le CRC-64 standard est décrit dans la norme ISO3309 et cette recette se contente de l’implémenter. Le polynôme générateur utilisé est
x64 + x4 + x3 + x + 1 (la section Voir aussi de cette recette vous indiquera où
obtenir les informations sur ce calcul).
Nous représentons le résultat sur 64 bits comme une paire de int Python, contenant
les 32 bits de poids faible et les 32 bits de poids fort du résultat. Pour pouvoir calculer
le CRC de façon incrémentale dans les cas où les données n’arrivent que par parties
à la fois, nous pouvons appeler la fonction crc64 en lui fournissant éventuellement
une « valeur initiale » pour la paire (crch, crcl), qui est censée être obtenue en
appelant crc64 sur les parties précédentes des données. Pour calculer le CRC d’un
110
Chapitre 2 — Fichiers
seul coup, il suffit de passer les données à la fonction (sous la forme d’une chaîne
d’octets) car, dans ce cas, nous initialisons le résultat à (0, 0) par défaut.
Voir aussi
W.H. Press, S.A. Teukolsky, W.T. Vetterling et B.P. Flannery, Numerical Recipes in C,
2e édition. (Cambridge University Press), p. 896 et ss.
Index
Symboles
#! (shebang), 69
''.join (jointure avec une chaîne vide), 9
''.join (jointure sur chaîne vide), 33
+, opérateur, 13
\ (anti-slash double), 4, 59
, (virgule), 15
\\ (anti-slash), 439
/ (barre de fraction), 59
% (opérateur de formatage des chaînes), 7,
13, 37
<<, opérateur, 83
4Suite, paquetage, 318
A
accès
à une base de données MySQL, 273
aux attributs des objets instances, 192
Access, 510
Active Server Pages (ASP), 381
ActivePython, 288
ActiveX Data Objects (ADO), 162
adaptateur, motif de conception, 88
__add__, méthode spéciale, 194
ADO (ActiveX Data Objects), 162
utilisation de Microsoft Jet, 287
affectation multiple, 422-425
affectation/test du résultat d’un expression,
181-183
affichage du contenu des curseurs de bases
de données, 283-285
algorithme de Luhn, 143
analyse lexicale
texte, 3
ancien style, classes, 196
anti-slash (\), 4, 59
anti-slash double (\\ double), 439
API portables, verrouillage de fichier, 104
append, méthode (objets listes), 15
applications
et conception des bases de données
relationnelles, 252
arborescences de répertoires
parcours, 89
archivage d’une arborescence dans un
fichier tar compressé, 81
*args, syntaxe, 163
arithmétique
décimale, 135
simulation de virgule flottante binaire,
416
ASCII, caractères dans les chaînes, 4
ASP (Active Server Pages), 381
assainit, fonction, 354
assert, 231
attributs
ajout à des classes, 202
cacher ceux fournis par délégation, 209
configuration
restriction dans les classes, 202
méthode __setattr__, 200
nommés, accès aux éléments d’un tuple,
212-214
rechercher dans les objets, 228-231
restriction d’ajout dans les classes, 202
authentication
navigation HTTPS via des proxies, 395
512
B
barre de fraction (/), 59
bases de données, 251, 270-293
affichage des curseurs, 283-285
applications fournissant un support des
transactions et un contrôle de la
concurrence, 253
persistance et compression, 259
problèmes de programmation, 251
relationnelles, 252
basestring, type, 9
Berkeley DB (Berkeley database),
persistance des données, 270-272
binaires, données envoyées sur la sortie
standard de Windows, 82
binaires, fichiers
écriture séquentielle d’octets, 67
lecture directe d’octets, 75
lecture séquentielle d’octets, 60
BLOB (binary large objects), 253
stockage
MySQL, 274
PostgreSQL, 275
SQLite, 277
boîtes aux lettres
POP3, messages mal formés, 357, 358
Borg, 192
Borg, classe, 238
remplacement du motif de conception
Singleton, 235-239
Borg, non-motif de conception, alternative
à, 237
boucles, désactivation pendant le débogage,
296
bsddb, module, 270-272
bsddb3, module, 270-272
__builtin__, module, 188
C
C++, syntaxe des entrées/sorties, 83
C, langage de programmation
cPickle, module prédéfini pour
stocker/restaurer les données, 253
cache
mise en cache automatique, 29
caches
valeurs des attributs, 472-474
caractères, 7
caractères de fin de ligne, 60
Celsius, températures, conversions, 198
Index
center, méthode (objets chaînes), 11
CGI (Common Gateway Interface), 303,
382
dépôt de fichiers, 386
programmes, 303
scripts (voir CGI, scripts)
tests, 382-384
CGI, scripts, 382
traitement des URL, 385-386
utilisation de Microsoft Jet via ADO,
287
chaînes
alignement, 11
contenant des fichiers zip, 79
extraction d’octets, 28
suppression des espaces, 12
tester les caractéristiques des chaînes, 9
(voir aussi chaînes de texte)
chaînes d’octets, 44
chaînes de texte, 4-7
alignement, 11
conversion
en minuscules/majuscules, 26
conversions
entre Unicode et normal, 45
expansion/compression des tabulations,
32
filtrage par rapport à un ensemble de
caractères, 22-25
insensibles à la casse, 51-57
interprétation des variables, 35
inversement par mots ou par caractères,
15
modification de l’indentation, 31
simplification de l’utilisation de la
méthode translate, 19
sous-chaînes, 28
substitutions, 38-41
suppression des espaces aux extrémités,
11
traitement caractère par caractère, 7
vérification
de la présence d’un ensemble de
caractères, 16-19
des fins, 41
du contenu, 25
characters, méthode, 322
chemins
modification dynamique, 95
Index
recherche de fichiers dans les
répertoires, 92-96
choix
de méthodes via un dictionnaire, 175
class
instruction, 192
__class__, attribut, 217
classes
ajout d’attributs, 202
chaînage des recherches dans des
dictionnaires, 204
classiques, fonctionnalité historique,
196
création d’instances, 192
définition par héritage, 194
fonctionnalité fournie à un ensemble de
classes, 195
instances, mises à jour lors de leur
rechargement, 496-500
instances, modification à la volée, 217
instanciation, 193
objets, 192
recherche de toutes les méthodes, 479
restriction de l’ajout d’attributs, 202
sauvegarde/restauration avec cPickle,
260-263
Singleton, 234
sous-classes, 194
(voir aussi métaclasses)
clés de dictionnaires
éviter les apostrophes lors de la
construction, 167
code
développement, les classes classiques
sont déconseillées, 244
maintenance par utilisation des classes
du nouveau style, 197
programmation, 197
et bases de données, 251
réutilisation via l’héritage, 194
code, objets
extraction de __init__, 262
modification dans un décorateur, 500
sérialisation, 265-267
codecs, module, affichage de caractères
Unicode sur la sortie standard, 47
collections.deque
héritage pour l’implémentation d’un
tampon circulaire, 223
513
COM
pilotage de ADO et Jet, 287
pilotage de Microsoft Word, 103
traitement de XML avec MSHTML, 336
commandes
exécution répétitive, 132
planification, 133
commentaires, trace pendant le débogage,
302-304
Common Gateway Interface (voir CGI)
compatibilité ascendante
classes classiques dans les nouveaux
codes, 244
héritage en Python, 196
compression
des objets, 259
et persistance, 259
conditionnelles, désactivation pendant le
débogage, 296
_const, classe, 202
const.py, module, 202
constantes, définition, 200
construction
d’une instance de classe vide, 216
de classes via des métaclasses, 197
dictionnaires, 167-170
listes, 7, 152, 156
content-type, vérification avec HTTP, 389
conversions
caractères en code numériques, 8
chaînes de texte, 45
en majuscules/minuscules, 26
de températures, 198
fuseaux horaires, 130
cookies
Internet Explorer, 398-399
traitement pendant la récupération des
pages web, 392-395
copies
objets, 149-151
__copy__, méthode, 216
copy, module, 149
copy.copy, fonction, 216
copy_reg, module, extension des modules
pickle/cPickle, 265
CoreGraphics, module, 100
courrier électronique, messages
avec Python 2.4, 355-356
courrier électronique
enregistrement sur disque, 376-377
514
mal formé, 356
boîtes aux lettres POP3, 357
cPickle, module, 253
classes et instances, 260-263
et compression, 259
sérialisation des données, 256-259
(voir aussi sérialisation)
CRC (cyclic redundancy check), 108-108
création
instances de classes, 192
cStringIO, module, 62, 79
cursor, objets, 279
cyclic redundancy check (CRC), 108-108
D
datagrammes, sockets (UDP), 341-343
surveillance du réseau, 364-366
utilisation pour SNTP, 346
dates/heures, 111-135
additionner des durées, 121
analyse floue des dates, 128
calcul
écart entre deux dates, 120
calculs
hier/demain, 117
jours fériés, 125-125
nombre de jours de travail, 123
conversion entre fuseaux horaires, 130
datetime, module, 113
lancement répété de commandes, 132
module time, 111-113
module timedelta
type timedelta, 117-120
obtenir l’heure à partir d’un serveur
SNTP, 345
planification de commandes, 133
timedelta, module
type timedelta, 122
trouver la date d’un jour d’une semaine
précédente, 119
vérifier l’heure d’été, 129
datetime, module, 113, 117, 121
calcul du nombre de jours de travail,
124
dateutil, module, 121
calcul du nombre de jours de travail,
124
recherche automatique des jours fériés,
125
Index
daylight saving time (DST), vérification,
129
DB, modules de l’API, style unique pour le
passage des paramètres, 285-287
db_row (Python Database Row Module),
282
débogage, 295-316
désactivation des conditionnelles et des
boucles, 296
fonction property, 215
lancement automatique d’un
débogueur après une exception non
capturée, 308-311
pile des appels, 305-308
ramasse-miettes, 299
test unitaires
exécution automatique, 311
tests unitaires
appartenance de valeurs à des
intervalles, 315-316
exécution, 309
trace des expressions/commentaires,
302-304
traitement des exceptions, 300-302
decimal, module, 114-117, 135-141
décorateurs, 463-509
modification des objets code, 500
__deepcopy__, méthode, 218
def, instruction de définition de méthodes,
193
__delattr__, méthode, 207
délégation, 195
dans les mandataires, 209-212
souplesse, 208
(voir aussi délégation automatique)
délégation automatique, 210
alternative à l’héritage, 206-209
enveloppes, 208
descripteurs, 463-509
description, attribut, 279
curseurs, 284
dict (type prédéfini), 167
fromkeys, classmethod, 177
dictionnaires
ajouter des entrées, 165
associer des noms de colonnes à des
valeurs d’indices, 279
chaînage des recherches, 204
choix de méthodes/fonctions, 175
clés (voir dictionnaires, clés)
Index
construction, 167-170
extraction de sous-ensembles, 170
inversion, 172
obtention des valeurs, 164
union et intersection, 177
(voir aussi mappings)
dictionnaires, clés
associer plusieurs valeurs, 173
dispatching
générateurs comme co-routines, 415
division, réelle vs. entière, 26
docstrings, 314
doctest, module, 296
DOM (Document Object Model), 318
données
Excel, accès à partir de Jython, 292
sauvegarde/restauration avec possibilité
de sélection/recherche, 272
sérialisation
module marshal, 254-256
modules pickle et cPickle, 256-259
structuration hiérarchique, 227
dtuple, module, 281
duck typing, 10
dump et dumps, fonctions
module marshal, 254-256
modules pickle et cPickle, 256-259
Dynamic IP (DNS), 372-376
E
E/S (entrées/sorties)
fichiers à accès direct, 75
syntaxe C++, 83
EAFP (easier to ask forgiveness than
permission), 10
écriture
dans un fichier, 67
email, paquetage
intégration de fichiers dans les messages
MIME, 349
suppression des pièces jointes, 352-354
encodage
détection automatique pour XML,
323-325
types, 46
Unicode pour XML/HTML, 49
entrée standard, lecture de caractères non
tamponnés, 98
entrées, fichiers, retour au début, 85-88
enumerate, fonction, 155
515
__eq__, méthode, ajout à la classe Borg, 236
erreurs, gestion
EAFP, approche, 10
erreurs, traitement, 295
dans l’analyse de XML, 330
via les exceptions, 300-302
espaces, suppression dans les chaînes, 12
estTexte, fonction, 26
État, motif de conception, 231-233
Excel (voir Microsoft Excel)
exceptions, gestion
à l’intérieur des expressions, 185
exceptions, traitement, 300-302
lancement automatique d’un
débogueur après une exception non
capturée, 308-311
exec, instruction, 187
expandtabs, méthode, 32
expat, parseur XML, 317
expressions
gestion des exceptions, 185
trace pendant le débogage, 302-304
vs. instructions, 487
expressions génératrices, 153
expressions régulières
expansion/compression des tabulations
dans les chaînes, 33
substitutions de sous-chaînes, 38
extend, méthode, 15
extensions de fichiers, modification dans
une arborescence, 90
extract_stack, fonction, 302
F
fabriques, fonctions
fermetures, 20
métaclasses, 493-496
sous-classes tuples, 211
Fahrenheit, températures, conversions, 198
FeedParser, module, 355, 356
fermetures, 21
Fibonacci, suite, 420
fichiers, 59-110
accès direct
entrées/sorties, 75
modification, 76
approche C++ pour les E/S, 83
archivage dans un fichier tar compressé,
81
compter les lignes, 70-73
516
dépôt avec CGI, 386
écriture, 67
entrée, retour au début, 85-88
extensions (voir extensions de fichiers)
intégration dans des messages MIME,
348-351
lecture, 63-66
d’une ligne spécifique, 69
mode binaire et mode texte, 60
modification des attributs avec
Windows, 101
modification dynamique du chemin de
recherche, 95
noms (voir noms de fichiers)
objets, 88
OpenOffice.org, extraction de texte, 102
parcours d’arborescences, 89
PDF (voir PDF, fichiers)
portabilité du code, 61-63
production de fichiers OPML, 400-403
pseudo-fichiers, 298
recherche
dans les répertoires, 92-96
recherche/remplacement de texte, 68
recherches
dans le chemin de recherche, 94
reprise d’un téléchargement par HTTP,
391
sauvegardes
numéros de versions dans les noms
de fichiers, 106
traitement des mots, 73
verrouillage
avec une API portable, 104
types de verrous, 105
Word, extraire le texte, 103
zip
dans une chaîne, 79
lecture, 78
filtrage
liste de sites FTP, 344
filtre_rdf, fonction, 333
fins de lignes, caractères, 65
FOAF (Friend-Of-A-Friend), 400
fonction remplacements_multiples, 39
fonctions
choix avec un dictionnaire, 175
polymorphisme, 229
portabilité, 63
Index
prédéfinies, appel de méthodes spéciales
dans un ordre spécifique, 194
formatage des chaînes, opérateur %, 7, 13
formatter.AbstractFormatter, classe, 56
Friend-Of-A-Friend (FOAF), 400
FTP, sites, filtrage, 344
G
Gadfly, 253
gc, module, 299
générateurs, 413-461
générique, programmation, 194
gestion des erreurs
dans l’encodage Unicode, 49-51
gestion des exceptions, 185
get, méthode, 35
et listes, 154
extraction de sous-ensembles de
dictionnaires, 171
valeurs des dictionnaires, 164, 164
__getattr__, méthode, 207, 210
valeur des attributs, 200
__getitem__, méthode, 162
_getS, méthod, 215
__getstate__, méthode, 217
getURLQualifiee, fonction, 386
getvalue, méthode, 301
gzip, module
et compression, 259
H
handlerTexte, classe, 322
__hash__, méthode, ajout à la classe Borg,
236
Haskell, langage de programmation, 153
héritage
et copie d’objets, 217
et réutilisation du code, 194
et Singletons, 234
et souplesse de la délégation, 208
inconvénients, 206
multiple, 247-251
ou délégation automatique, 206-209
pour le polymorphisme, 194
(voir aussi héritage multiple)
HTML
conversion en texte avec Unix, 55
encodage Unicode, 49
envoi de courrier électronique, 346-348
(voir aussi XML)
Index
htmlentitydefs, module, 49
HTTP
surveillance des réseaux, 364-366
vérification du type de contenu, 389
HTTPS, navigation et authentification sur
un proxy, 395
I
IDLE (Integrated Development
Environment)
shell graphique pour explorer la POO
en Python, 194
immutabilité, 201
__init__, méthode, 52
constructeurs des instances de classes,
193
court-circuit, 217
initialisateurs des instances de classe,
193
redéfinition, 217
, 262
appel des super-classes qui la définit,
244-247
extraction de l’objet code, 243
initialisation automatique des variables
d’instance, 242-244
initialisation des variables d’instance à
partir de méthodes __init__, 242-243
instances
objets, 192
sauvegarde/restauration avec cPickle,
260-263
tester des changements d’état, 224-227
instantane, méthode, 226
Internet Relay Chat (IRC), 376-376
intersection, méthode, 178
intervalles, appartenance de valeurs dans
les tests unitaires, 315-316
introspection
codage et, 247
IRC (Internet Relay Chat), connexion,
376-377
isinstance, méthode, 9
islower, méthode, 27
isSSL, fonction, 386
istitle, méthode, 27
isupper, methode, 27
itemgetter, fonction, 214
iter, fonction, 159
__iter__, méthode, 63
517
iterable, mappings, 206
itérateurs, 413-461
iteritems, méthode, 173
itertools, module, 17, 17
construction de dictionnaires, 168
inversion de dictionnaires, 172, 172
itertools.ifilter, 17
izip, 168, 172
J
JDBC (Java Database Connectivity), accès à
partir d’une servlet Jython, 289-292
join, méthode, 6, 14
jointure avec une chaîne vide (’’.join), 9, 33
jouets, programmes, 251
journaux
stockage des informations, 221-224
Jython
extraction de données Excel, 292
servlets
connexion à une base de données
JDBC, 289-292
exécution, 396
K
kelvin, températures, conversions, 198
KeyError, exception, 205
**kwds, syntaxe, 163
L
langages de programmation, état et
comportement, 192
LBYL (Look Before You Leap), vérifier les
attributs d’un objet, 228
LDAP, accès aux serveurs, 378
ldap, extension, 378
ldap, module, 378
lecture
à partir d’un fichier zip, 78
caractères non tamponnés sur l’entrée
standard, 98
de données/texte dans un fichier, 63-66
ligne spécifique d’un fichier texte, 69
liaison des attributs des objets instances,
192
liées, méthodes, 42
linecache, module, 69
518
Linux
mesure de l’utilisation mémoire, 297
listes
choisir aléatoirement des éléments, 184
construction, 7, 152, 156
de lignes, suppression/modification de
l’ordre des colonnes, 160
objets, 15
renvoi des éléments, 154
listes en compréhension, 28
listes en intension
accès aux sous-chaînes, 28
construction d’un dictionnaire, 172
suppression/modification de l’ordre des
colonnes dans une liste de lignes,
160
traduction de Haskell en Python, 153
ljournaux, centralisés, 295
ljust, méthode (objets chaînes), 11
locale, module, 139
localisation
alphabets de l’Europe occidentale, 4
traitement des caractères non ASCII,
43-45
locals, fonction, 243
lock, fonction, 105
logging, module, 342
Look Before You Leap (LBYL), vérifier les
attributs d’un objet, 228
lower, méthode, 26
__lshift__, 83
lstrip, méthode, 12
M
Mac OS X
caractères de fin de ligne, 65
compter les pages d’un fichier PDF, 100
makefile, méthode, source de texte, 4
maketrans, fonction, 19
filtrage des chaînes, 23
mandataires, 210
mandataires, délégation des méthodes
spéciales, 209-212
MapChainee, classe, 205
mappings, 204
partiels transformés en complets, 205
marshal, module, 253, 266
cas d’utilisation, 272
limitations, 266
sérialisation des données, 254-256
Index
mémoire
économie et implémentation de tuples
sous forme d’éléments nommés, 214
et tampons circulaires, 222
mesure de son utilisation avec Linux,
297
recherche des fuites, 299
transformations utilisant des fichiers, 3
mémoïsation, 29
implémentation de la méthode
__deepcopy__, 218
métaclasses, 197, 463-509
méthodes
appels d’autres méthodes sur la même
instance, 195
cacher celles fournies par délégation,
209
choix avec un dictionnaire, 175
considérées comme des attributs, 207
de classes, recherches, 479
définition du comportement des objets
instances, 193
délégation de l’opération à la même
méthode dans la super-classe, 195
des objets chaînes, 6
des sous-classes, redéfinition des
méthodes de la super-classe, 195
liées, 42
non liées, 42
spéciales de classes, 194
méthodes liées
contenues par les objets, sérialisation,
263-265
maintenir des références sans empêcher
le travail du ramasse-miettes, 218
références faibles, 220
méthodes spéciales
appelées dans un ordre spécifique par
les opérations/fonctions prédéfinies,
194
délégation dans les mandataires,
209-212
Microsoft Access (voir Access)
Microsoft Excel
analyse de XML, 328
extraction des données avec Jython, 292
Microsoft Jet, 510
utilisation via ADO, 287
Microsoft ODBC, standard, 252
Microsoft SQL Server, 253
Index
Microsoft Word (voir Word)
MIME (Multipurpose Internet Mail
Extensions), 348
MIME, messages
intégration de fichiers, 348-351
multi-volet, décomposition, 351-352
mimetools, module, 346
MimeWriter, module, 346
mise en cache automatique, 29
mixin, classe
appels coopératifs aux super-classes, 247
fonctionnalité fournie à un ensemble de
classes, 195
tester les changements d’état des
instances, 224
MixinVerifModif, class, 226
mode binaire et mode texte des fichiers, 60
modification des fichiers à accès direct, 76
modules
avantages par rapport aux objets de la
POO, 192
définitions de classes, contenant une
instruction d’affectation, 197
et chemin de recherche de Python, 95
vérifier qu’un nom est défini, 187
monétaires, opérations, 135-145
calculatrice en Python, 140-143
formatage des décimaux, 137-140
surveillance des taux de changes, 144
utilisation de l’arithmétique décimale,
135
vérification des sommes de contrôles
des cartes bancaires, 143
moneyfmt, fonction, 137
motifs de conception, 231-231
adaptateur, 88
État, 231-233
Monostate, 238
objet nul, 239-242
orientés objet, 192
patron de méthode, 195
Singleton, 192, 233-239
Stratégie, 232
MSHTML, 336
msvcrt, module, 82, 99
Multipurpose Internet Mail Extensions
(MIME), 348
MySQL, 253
accès aux bases de données, 273
stockage de BLOB, 274
519
MySQLdb, module, 273
stockage de BLOB dans MySQL, 274
N
Network News Transfer Protocol (NNTP),
340, 340
__new__, méthode, 214
__new__ staticmethod, méthode, 233
NNTP (Network News Transfer Protocol),
340, 340
nobuffer, méthode, 87
nommés, attributs, accès aux éléments d’un
tuple, 212-214
noms de fichiers, versions, 106
non liées, méthodes, 42
NoNewAttrs, classe, 202
nouveau style, classes, 196
Null, classe, 239
voir aussi Objet nul, motifs de
conception, 239
O
Objet nul, motif de conception, 239-242
objets
compression, 259
contenant des méthodes liées d’autres
objets, sérialisation, 263-265
copie, 149-151
copie de surface, 218
copie en profondeur, 218
copie rapide, 216
cycles de références, 299
de première classe, 192
description de leur création, 192
docstrings, 314
en Python, 192
état, 231-233
fichiers (voir fichiers, objets)
itérables, 159
modifications avec le module shelve,
267-269
sérialisation des objets code, 265-267
tester les caractéristiques des chaînes, 9
vérifier les attributs, 228-231
octets ou caractères, 1
calcul d’un CRC-64 sur un flux, 108-110
extraction des chaînes, 28
écriture séquentielle dans un fichier
binaire, 67
520
lecture directe dans un fichier binaire,
75
lecture séquentielle dans des fichiers
binaires, 60
ODBC (Open Database Connectivity), 252
open, 59
lecture dans un fichier, 64
Open Database Connectivity (voir ODBC)
Openldap, API C, 378
OpenOffice.org, fichiers, extraction de texte,
102
opérateur de formatage des chaînes (%), 7,
13, 37
opérations
appel de méthodes spéciales dans un
ordre spécifique, 194
modification de l’état, vérification des
attributs nécessaires, 228
operator, module, 142, 214
OPML (Outline Processor Markup
Language), 400
fichiers, 400-403
Oracle, 253
ordinateurs, surveillance, 360
os, module, 55, 59
parcours d’arborescences, 89
outils de conception industrielle et bases de
données relationnelles, 252
Outline Processor Markup Language
(OPML), 400
P
paramètres, style identique pour tous les
modules de l’API DB, 285-287
parser, module, 128
parseurs
XML avec MSHTML, 336
patron de méthode, motif de conception,
195
PDF, fichiers, compter les pages sur Mac OS
X, 100
performances
ajouter des entrées à un dictionnaire,
166
inverse_dict_rapide vs. inverse_dict, 173
modification du chemin de recherche,
95
plusieurs morceaux de chaînes dans les
séquences, 14
transformations utilisant des fichiers, 3
Index
unions/intersections de dictionnaires,
177
persistance, 251-269
et compression, 259
pickle, module, 253
cas d’utilisation, 272
sérialisation des données, 256-259
(voir aussi sérialisation)
pièces jointes, suppression, 352-354
pile des appels
obtention d’information pendant le
débogage, 305-308
polymorphisme
avantage de la POO, 194
et fonctions Python, 229
par signature, 194
POO (programmation orientée objet), 175,
191-251
avantage du polymorphisme, 194
implémentation en Python, 191
pop, méthode
extraction de sous-ensembles de
dictionnaires, 171
obtention des valeurs d’un dictionnaire,
165
POP3, consultation interactive, 357-359
poplib, module, 357
portabilité du code, 61-63
portalocker.py, module, 105
ports réseaux, suivi/redirection, 367-369
PostgreSQL, stockage de BLOB, 275
précision bornée, 114
prédicats, 18
expansion des éléments d’une liste, 158
printf, fonction, 183
programmation, 251
programmation orientée objet (voir POO)
property, fonction, 214
propriétés, éviter les accesseurs
passe-partout, 214
proxy, fonction, 211
proxy, tunnel SSL, 369-372
pseudo-fichiers, lecture/analyse du contenu,
298
psycopg, module, 275
Py-DBAPI (Python DB Application
Programming Interface), 253
pysqlite, module, 285
Python
bénéfices de la simplicité, 191
Index
comme une calculatrice, 140-143
conversion d’un document XML en
arborescence d’objets, 325-327
distributions, ActivePython, 288
fonction printf de C, 183
fonctionnalités POO, 191
interface pour accéder aux bases de
données, 253
interface pour accéder aux bases de
données relationnelles
alternatives, 253
outils, 463-509
raccourcis de programmation, 147-189
support de plusieurs paradigmes, 192
(voir aussi Python 2.3, Python 2.4)
Python 2.3
caractère de remplissage dans
l’alignement des chaînes, 11
interprétation des variables dans les
chaînes, 35
utilisation du module decimal, 141
(voir aussi Python)
Python 2.4
caractère de remplissage dans
l’alignement des chaînes, 11
et courrier électronique, 355-356
expression génératrices, 171
interprétation des variables dans les
chaînes, 35-38
module doctest, utilisé avec unittest,
312
parseur de courrier électronique, 354
(voir aussi Python)
Python Database Row Module (db_row),
282
Python DB Application Programming
Interface, 253
PyWin32, paquetage, 288
PyXML, paquetage, 318
Q
Quixote, 381
R
raccourcis en Python, 147-189
ramasse-miettes
cycles, 299
débogage, 299
maintenir les références à des méthodes
liées sans l’empêcher, 218
521
random, module, 184
Rankine, températures, conversions, 198
rassemblement d’éléments nommés,
179-181
re, module
remplacement de plusieurs motifs dans
les chaînes, 39
traitement des chaînes, 6
read, méthode, 61, 343
lecture dans un fichier, 64-66
lecture des objets sérialisés sur disque,
260
readLines, méthode, 61, 64
recherche
date d’un jour d’une semaine
précédente, 119
de cookie Internet Explorer, 398-399
de fichiers dans les répertoires, 92-96
de toutes les méthodes des classes, 479
de toutes les méthodes des classes, 479
des sous-séquences dans les séquences,
158
recherche/remplacement de texte dans
les fichiers, 68
récupération incrémentale des données
d’une base de données, 441
redéfinition des méthodes, 194
ref, classe, 220
referenceError, exception, 220
références faibles, 218
vers des méthodes liées, 220
références, cycles, 299
RegarderAvantDeSauter, classe, 245
relatifs, chemins, calcul, 96
relationnelles, bases de données, 252
approches hybrides, 253
implémentations avec interface ODBC,
253
implémentations des éditeurs
principaux, 252
inadéquation à certaines applications,
252
sauvegarde/restauration des données
avec possibilité de
sélection/recherche, 272
systèmes logiciels trois tiers, 252
répertoires
arborescences (voir arborescences de
répertoires)
522
modification des extensions de
fichiers, 90
calcul du chemin relatif, 96
recherche de fichiers, 92-96
replace, méthode, 53, 68
__repr__, méthode, 214
réseau, programmation, 339-379
detection des ordinateurs inactifs,
359-364
DNS dynamique, 372-376
envoi de messages par datagrammes,
341-343
surveillance avec HTTP, 364-366
resource, module, 298
rjust, méthode (objets chaînes), 11
rrule.count, méthode, 121
rstrip, méthode, 12
S
s.count, méthode, 6
s.isdigit, méthode, 6
s.toupper, méthode, 6
sauvegardes
numéros de versions dans les noms de
fichiers, 106
SAX API (parseur XML), 317
SAX API, documents bien formés, 319
SAX, parseur, fusion d’événements textes
contigus, 333-336
scalaires, 158
sched, module, planification de
commandes, 133
Secure Socket Layer (SSL), 369-372
Secure Socket Layer/Transport Layer
Security (SSL/TLS), 372
seek, méthode, 61
fichiers à accès direct, 77
self.qquechose, syntaxe, 193
séquences, 14
accès facilité, 281-282
imbriquées, aplatir, 158-160
parcours des éléments, 155
séquences génétiques, archivage dans une
base de données relationnelle, 253
sérialisation
objets code, 265-267
objets contenant des méthodes liées,
263-265
sérialisation des données
module marshal, 254-256
Index
modules pickle et cPickle, 256-259
sérialisation des objets codes avec marshal,
266
serveurs, LDAP, 378
servlets, codage en Jython, 396
set, méthode, 18
Set, type, 178
__setattr__, méthode, 202, 207
initialisation des attributs, 200
setdefault, méthode
ajouter des entrées à un dictionnaire,
165
setdefault, méthode, ajout d’entrées dans
un dictionnaire, 165
sets, module, 98, 178
finding sets/unions of dictionaries, 177
__setstate__, méthode, 217
shebang (#!), 69
shelve, module, modification d’objets,
267-269
signatures, méthodes avec la même, 194
Simple Mail Transfer Protocol (SMTP), 340
Simple Network Time Protocol (SNTP),
345
Singleton, motif de conception, 192, 233
alternative à, 237
remplacement par la classe Borg,
235-239
Singletons, 234
__slots__, méthode
restriction de l’ajout d’attributs, 203
SMTP (Simple Mail Transfer Protocol), 340
SNTP (Simple Network Time Protocol),
345
socket, module, 340
somme de contrôle cyclique (voir CRC)
sortie standard
affichage de l’Unicode, 47
utilisation de printf, 183
sous-chaînes, accès, 28-31
sous-classes, 194
méthodes, 195
spéciales, méthodes
définitions de classes, 194
split, méthode, 33
lecture dans un fichier, 64
traitement des mots dans les fichiers, 74
splitlines, méthode, 32
lecture dans un fichier, 64
SQL, 252
Index
implémentations, 253
représentations XML stockées dans des
bases de données, 253
SQLite, 253
stockage de BLOB, 277
sqlite.encode, insertion de BLOB dans
SQLite, 277
SSL (Secure Socket Layer), tunnel à travers
un proxy, 369-372
SSL/TLS (Secure Socket Layer/Transport
Layer Security), 372
Stratégie, motif de conception, 232
strftime, function, 112
string, module, 6
filtrage des chaînes, 23
StringIO, module, 62, 80
strip, méthode, 12
strptime, function, 112
sub, méthode, pour les substitutions de
chaînes, 38
substitute, méthode, 38
sum, fonction, 122
super, méthodes
délégation à la super-classe, 195
super-classes
appel de la méthode __init__, 244-247
coopération, 247-249
délégation, 195
méthodes, 195
SuperMixin, class, 249
__slots__, fonction, 214
superTuple, fonction, 214
surveillance d’ordinateurs, 360
Sybase, 253
T
tableaux
transposition de tableaux à deux
dimensions, 162
tabulations dans les chaînes,
expansion/compression, 32
tampons circulaires, 221-224
tar, fichiers archives compressés, 81
tarfile, module, 81
taux de changes, surveillance, 144
tell, méthode, 61
retour au début d’un fichier, 88
temperatures, conversions, 198
temps (voir dates/heures)
termios, module, 99
523
tests, 295-316
CGI, 382-384
des caractéristiques des chaînes dans les
objets, 9
traitement des exceptions, 300-302
unitaires
appartenance de valeurs à des
intervalles, 315-316
exécution, 309
exécution automatique, 311
texte, 1-57
analyse lexicale, 3
chaînes, 1-7
conversion de document HTML avec
Unix, 55
extraction
de fichiers OpenOffice.org, 102
des fichiers Word, 103
lecture dans un fichier, 63-66
recherche/remplacement dans les
fichiers, 68
sources, 3
traitement, 1-7
texte brut, 4
texte normal
conversion en Unicode, 45
There’s More Than One Way To Do It
(TMTOWIDI), 147
time, module, 111-113
timedelta, module (datetime), 117-120, 122
timedelta, module, calcul de dates, 117
timeit, module, 73
TMTOWTDI (There’s More Than One Way
To Do It), 147
traceback, module, 302
traceback.print_exc, fonction, 301
traitement
des mots dans un fichier, 73
des textes internationaux avec Unicode,
43-45
du texte, 1-57
du texte non ASCII, 43-45
traitement du texte
chaînes, caractère par caractère, 7
conversion de caractères en Unicode, 8
opérations de base, 2
translate, méthode, 19, 19, 19-26
filtrage des chaînes, 23
simplification de l’utilisation, 19
524
try/except, instruction, dans les expressions,
186
tty, module, 99
TupleDescriptor, classe, 281
tuples, implémentation avec des éléments
nommés, 212-214
type de données numérique et décimal, 114
type prédéfini, héritage, 197
TypeError, exception levée par
inspect.getargspec, 246
types, vérification, 10
U
UDP (user datagram protocol), 341
Unicode, 4
affichage sur la sortie standard, 47
conversion
caractères en codes numériques, 8
conversions
en texte normal, 45
encodage
gestion des erreurs, 49-51
pour XML/HTML, 49
traitement des textes internationaux,
43-45
uniform, fonction, 184, 184
unitaires, tests, 296
appartenance de valeurs à des
intervalles, 315-316
exécution, 309
exécution automatique, 311
utilisation de doctest avec unittest et
Python 2.4, 312
unittest, module
utilisation avec doctest et Python 2.4,
312
Unix
caractères de fin de ligne, 65
conversion de documents HTML en
texte, 55
unObjet.qquechose, syntaxe, 193
upper, méthode, 26
URL
récupération de documents sur le Web,
343
traitement dans les scripts CGI, 385-386
urllib, module, 340
urllib2, module, 340
urlopen, fonction, 343
user datagram protocol (UDP), 341
Index
V
variables
de module, 200
interprétation dans les chaînes, 35
virgule (,), 15
virgule flottante, 114
arithmétique, 135
virus, 354
W
weakref, module, 218, 220, 299
Web
obtention d’un document à partir de
son URL, 343
web, pages et traitement des cookies,
392-395
web, programmation, 381-412
web, serveurs, 381
WebWare, 381
win32api, module, 101
Windows
caractères de fin de ligne, 65
modification des attributs de fichiers,
101
sortie standard, envoi de données
binaires, 82
utilisation de MSHTML pour traiter
XML, 336
write, méthode, 3
écriture dans les fichiers, 60
writelines, méthode, écriture dans un
fichier, 67
writestr, méthode, lecture d’un fichier zip,
79
X
XML
accès aux données structurées sous
forme lisible, 253
encodage Unicode, 49
utilisation de MSHTML, 336
XML, balises, 320
XML, documents
comptage des balises, 320
conversion en arborescence d’objets
Python, 325-327
extraction du texte, 321
validation, 330
XML, traitement, 317-337
analyse du XML de Microsoft Excel, 328
Index
détection automatique de l’encodage,
323-325
filtrage des éléments/attributs d’un
espace de noms, 331-333
gestion des erreurs, 330
suppression des nœuds ne contenant
que des espaces, 327
vérifier qu’un document est bien formé,
319
XML, validation, 320
xml.sax.saxutils module, 334
XMLFilterBase, class, 334
525
XMLGenerator, classe, 333
xproperty, fonction, 215
Z
Z-Object Database (ZODB), 253
zip, 168
zip, fichiers
dans une chaîne, 79
lecture, 78
zipfile, module, 78
ZODB (Z-Object Database), 253
Zope, 381
Téléchargement