Sous MacOS X, vous avez deux possibilités, garder la version préinstallée ou installer une nouvelle version. Vous préfèrerez sans doute
cette dernière solution.
Téléchargez le dernier RPMPython en allant sur http://www.python.org/ftp/python/2.3.3/ et en
sélectionnant le numéro de version le plus haut, puis en sélectionnant
le sous-répertoire rpms/ de cette version.
Téléchargez ensuite le RPM ayant le plus haut
numéro de version. Vous pouvez l'installer avec la commande
rpm, comme ci-dessous :
Si vous préférez installer à partir du source, vous pouvez
télécharger le code source de Python à
partir de http://www.python.org/ftp/python/2.3.3/. Sélectionnez
le numéro de version le plus haut de la liste, téléchargez le fichier
.tgz et faites la séquence habituelle
configure, make,
make install.
Python dispose de fonctions comme la
plupart des autre langages, mais il n'a pas de fichiers d'en-tête
séparés comme C++ ou de
sections interface/implementation
comme Pascal. Lorsque vous avez besoin
d'une fonction, vous n'avez qu'à la déclarer et l'écrire.
Les fonctions Python
n'ont pas de begin ou end
explicites, ni d'accolades qui pourraient marquer là ou commence et ou
se termine le code de la fonction. Le seul délimiteur est les deux
points («:») et l'indentation du code
lui-même.
Les modules Python sont des objets et ils ont de nombreux attributs utiles. C'est un aspect que vous pouvez utiliser pour tester facilement
vos modules au cours de leur écriture. Voici un exemple qui utilise l'astuce if__name__.
Les listes sont le type de données à tout faire de
Python. Si votre seule expérience des
listes sont les tableaux de Visual Basic ou
(à Dieu ne plaise) les datastores de
Powerbuilder, accrochez-vous pour les
listes Python.
Python dispose de
variables locales et globales comme la plupart des autres langages,
mais il n'a pas de déclaration explicite des variables. Les variables
viennent au monde en se voyant assigner une valeur et sont
automatiquement détruites lorsqu'elles se retrouvent hors de
portée.
Python supporte le
formatage de valeurs en chaînes de caractères. Bien que cela peut
comprendre des expression très compliquées, l'usage le plus simple
consiste à insérer des valeurs dans des chaînes à l'aide
de marques %s.
Une des caractéristiques les plus puissantes de
Python est la list
comprehension (création fonctionnelle de listes) qui
fournit un moyen concis d'appliquer une fonction sur chaque élément
d'une liste afin d'en produire une nouvelle.
Nous avons une liste de paires clé-valeur sous la forme
clé=valeur et nous voulons les assembler au sein
d'une même chaîne. Pour joindre une liste de chaînes en une seule,
nous pouvons utiliser la méthode join d'un objet chaîne.
Voici un programme Python complet et
fonctionnel. Vous devriez en comprendre une grande partie rien qu’en
le lisant. Les lignes numérotées illustrent des concepts traités dans
Chapitre 2, Votre premier programme Python. Ne vous inquiétez pas si le reste du
code a l’air intimidant, vous en apprendrez tous les aspects au cours
de ce chapitre.
Python permet aux arguments de
fonction d’avoir une valeur par défaut, si la fonction est appelée
sans l’argument il a la valeur par défaut. De plus, les arguments
peuvent être donnés dans n’importe quel ordre en utilisant les
arguments nommés. Les procédures stockées de Transact/SQL sous SQL Server
peuvent faire la même chose, si vous êtes un as des scripts sous SQL Server, vous pouvez survoler cette partie.
Python a un petit ensemble de
fonctions prédéfinies très utiles. Toutes les autres fonctions sont
réparties dans des modules. C’est une décision de conception
consciente, afin d’éviter au langage de trop grossir comme d’autres
langages de script (au hasard, Visual Basic).
Vous savez déjà que les
fonctions Python sont des objets. Ce
que vous ne savez pas, c’est que vous pouvez obtenir une référence à
une fonction sans connaître son nom avant l’exécution, à l’aide de la
fonction getattr.
Comme vous le savez, Python a des
moyens puissant de mutation d’une liste en une autre, au moyen des
list comprehensions (Section 3.6, «Mutation de listes»). Cela peut être
associé à un mécanisme de filtrage par lequel certains éléments sont
modifiés alors que d’autres sont totalement ignorés.
En Python, and et
or appliquent la logique booléenne comme vous
pourriez l’attendre, mais ils ne retournent pas de valeurs booléennes,
ils retournent une des valeurs comparées.
Python permet une syntaxe
intéressante qui vous laisse définir des mini-fonctions d’une ligne à
la volée. Empruntées à Lisp, ces fonctions
dites lambda peuvent être employées partout où une
fonction est nécéssaire.
La dernière ligne du code, la seule que nous n’ayons pas encore
déconstruite, est celle qui fait tout le travail. Mais arrivé à ce
point, le travail est simple puisque tous les éléments dont nous avons
besoin sont disponibles. Les dominos sont en place, il ne reste qu’à
les faire tomber.
Voici un programme Python complet et
fonctionnel. Lisez les doc string
du module, des classes et des fonctions pour avoir un aperçu de ce que
ce programme fait et de son fonctionnement. Comme d'habitude ne vous
inquiétez pas de ce que vous ne comprenez pas, la suite du chapitre
est là pour vous l'expliquer.
Python fournit deux manières
d'importer les modules. Les deux sont utiles et vous devez savoir
quand les utiliser. Vous avez déjà vu la première, import module,
à la Section 2.4, «Tout est objet».
La deuxième manière accomplit la même action avec des
différences subtiles mais importante dans son fonctionnement.
Python est entièrement orienté objet
: vous pouvez définir vos propres classes, hériter de vos classes ou
des classes prédéfinies et instancier les classes que vous avez
défini.
L'instanciation de classes en Python
est simple et directe. Pour instancier une classe, appelez simplement
la classe comme si elle était une fonction, en lui passant les
arguments que la méthode __init__ définit. La
valeur de retour sera l'objet nouvellement créé.
Comme vous l'avez vu, FileInfo est une
classe qui se comporte comme un dictionnaire. Pour voir ça plus en
profondeur, regardons la classe UserDict dans
le module UserDict, qui est
l'ancêtre de notre classe FileInfo. Cela n'a
rien de spécial, la classe est écrite en
Python et stockée dans un fichier
.py, tout comme notre code. En fait, elle est
stockée dans le répertoire lib
de votre installation Python.
En plus des méthodes de classe ordinaires, il y a un certain
nombre de méthodes spéciales que les classes
Python peuvent définir. Au lieu d'être
appelées directement par votre code (comme les méthodes ordinaires)
les méthodes spéciales sont appelées pour vous par
Python dans des circonstances particulières
ou quand une syntaxe spécifique est utilisée.
Il y a d'autres méthodes spéciales que
__getitem__ et __setitem__.
Certaines vous laissent émuler des fonctionnalité dont vous ignorez
encore peut-être tout.
Vous connaissez déjà les données attributs, qui
sont des variables appartenant à une instance particulière d'une
classe. Python permet aussi les attributs
de classe, qui sont des variables appartenant à la classe
elle-même.
Contrairement à la plupart des langages, le caractère privé ou public d'une fonction, d'une méthode ou d'un attribut est déterminé
en Python entièrement par son nom.
Voila pour ce qui est des chicanes techniques des objets. Vous verrez une application réelle des méthodes de classe spéciales
au Chapitre 12, qui utilise getattr pour créer un mandataire d'un service web distant.
Python a une fonction prédéfinie,
open, pour ouvrir un fichier sur le disque.
open retourne un objet-fichier qui possède des
méthodes et des attributs pour obtenir des informations et manipuler
le fichier ouvert.
Comme la plupart des langages, Python
a des boucles for. La seule raison pour laquelle
vous ne les avez pas vues jusqu’à maintenant est que
Python sait faire tellement d’autre choses
que vous n’en avez pas besoin aussi souvent.
Les modules, comme tout le reste en
Python, sont des objets. Une fois qu'il a été importé,
vous pouvez toujours obtenir une référence à un module à travers le
dictionnaire global sys.modules.
Le module os.path a de nombreuses fonctions pour manipuler les chemins de fichiers et de répertoires. Ici nous voulons gérer les chemins et
lister le contenu d'un répertoire.
A nouveau, tous les dominos sont en place. Nous avons vu comment
chaque ligne de code fonctionne. Maintenant prenons un peut de recul
pour voir comment tout cela s’assemble.
Si ce que vous essayez de faire peut être accompli avec les fonctions de chaînes, utilisez-les. Elles sont rapides et faciles
à comprendre et il y a beaucoup d'avantages à un code rapide, simple et lisible. Mais si vous vous rendez compte que vous
utilisez un grand nombre de fonctions de chaînes différentes avec des instruction if pour les cas particulier, ou si vous les associez à des fonctions split et join et à des list comprehension de manière complexe et illisible, vous devez vous tourner vers les expressions régulières.
Cette série d’exemples est inspirée d’un problème réel que j’ai
eu au cours de mon travail, l’extraction et la standardisation
d’adresses postales exportées d’un ancien système avant de les importer
dans un nouveau système (vous voyez, je n’invente rien, c’est
réellement utile). L’exemple suivant montre comment j’ai abordé ce problème.
Dans la section précédente, nous avons vu un motif dans lequel le même caractère pouvait être répété jusqu’à trois fois. Il y a une autre manière d’exprimer
cela dans les expressions régulière, que certaines personnes trouvent plus lisible. D’abord, revenons sur la méthode que nous
avons utilisé dans l’exemple précédent.
Jusqu'à maintenant, vous n'avez vu que ce que j'appellerais des expressions régulières «compactes». Comme vous l'avez vu, elles sont difficiles à lire et même si vous comprenez ce qu'une d'entre elles fait, rien n'assure
que vous pourrez la comprendre dans six mois. Ce qu'il faut, c'est une documentation intégrée.
Jusqu'ici nous nous sommes concentrés sur la reconnaissance de motifs complets, le motif est reconnu ou non. Mais les expressions
régulières sont beaucoup plus puissantes que cela. Lorsqu'une expression régulière reconnaît un motif, nous pouvons sélectionner
certaines parties du motif. Nous pouvons savoir ce qui a été reconnu et à quel endroit.
Nous n'avons vu que la pointe de la partie émergée de l'iceberg des possibilités offertes par les expressions régulières.
En d'autres termes, même si vous vous sentez totalement dépassé, vous n'avez encore rien vu.
Je vois souvent sur comp.lang.python
des questions comme «Comment faire une liste de tous les
[en-têtes|images|liens] de mon document HTML ?» «Comment faire pour
[parser|traduire|transformer] le texte
d’un document HTML sans toucher aux balises ?»
«Comment faire pour [ajouter|enlever|mettre entre
guillemets] des attributs de mes balises HTML d’un
coup ?» Ce chapitre répondra à toutes ces questions.
Le traitement du HTML est divisé en trois étapes : diviser le HTML en éléments, modifier les éléments et reconstruire le HTML à partir des éléments. La première étape est réalisée par sgmllib.py, qui fait partie de la bibliothèque standard de Python.
La clé de la compréhension de ce chapitre est de réaliser que le
HTML n’est pas seulement du texte, c’est du texte
structuré. La structure est dérivée de la séquence plus ou moins
hiérarchique de balises de début et de fin. Habituellement, on ne
travaille pas de cette manière sur du HTML, on
travaille textuellement dans un éditeur de texte ou
visuellement dans un navigateur ou un éditeur de
pages web. sgmllib.py présente
le HTML de manière
structurelle.
Pour extraire des données de documents HTML,
on dérive une classe de SGMLParser et on
définit des méthodes pour chaque balise ou entité que l’on souhaite
traiter.
SGMLParser ne produit rien de lui même.
Il ne fait qu’analyser et appeler une méthode pour chaque élément
intéressant qu’il trouve, mais les méthodes ne font rien.
SGMLParser est un
consommateur de HTML : il
prend du code HTML et le décompose en petits
éléments structurés. Comme nous l’avons vu dans la section précédente, on peut dériver
SGMLParser pour définir une classe qui trouve
des balises spécifiques et produit quelque chose d’utile, comme une
liste de tous les liens d’une page web. Nous allons maintenant aller
un peu plus loin en définissant une classe qui prends tout ce que
SGMLParser lui envoi et reconstruit
entièrement le document HTML. En termes techniques,
cette classe sera un producteur de
HTML.
Laissons de coté le traitement du HTML une minute pour parler de la manière dont Python gère les variables. Python a deux fonctions prédéfinies permettant d’accéder aux variables locales et globales sous forme de dictionnaire : locals et globals.
Une question courante sur comp.lang.python
est la suivante : «J’ai plein de documents
HTML avec des valeurs d’attributs sans guillemets et
je veux les mettre entre guillemets. Comment faire ?»[5] (C’est en général du à un chef de projet qui pratique la
religion du HTML-est-un-standard et proclame que
toutes les pages doivent passer les tests d’un validateur
HTML. Les valeurs d’attributs sans guillemets sont
une violation courante du standard HTML). Quelle
que soit la raison, les valeurs d’attributs peuvent se voir dotées de
guillemets en soumettant le HTML à
BaseHTMLProcessor.
Dialectizer est un descendant simple (et
humoristique) de BaseHTMLProcessor. Il procède
à une série de substitutions dans un bloc de text, mais il s’assure
que tout ce qui est contenu dans un bloc
<pre>...</pre>
passe sans altération.
Python vous fournit un outil
puissant, sgmllib.py, pour
manipuler du code HTML en transformant sa structure
en modèle objet. Vous pouvez utiliser cet outil de nombreuses
manières.
Il y a fondamentalement deux manières de travailler avec XML. L'une est appelée SAX («Simple API for XML») et fonctionne en lisant les données XML au fur et à mesure et en appelant une méthode chaque fois qu'un élément est rencontré. (Si vous lisez Chapitre 8, Traitement du HTML, cela devrait vous paraître familier, parce que c'est la façon dont le module sgmllib fonctionne.) L'autre est appelée DOM («Document Object Model»), et fonctionne en lisant le document XML dans son entier et en en créant une représentation interne au moyen de classes Python natives reliées dans une structure arborescente. Python dispose de modules standards pour chacun de ces deux traitements, mais ce chapitre ne concernera que l'utilisation du DOM.
Analyser un document XML est pour l'heure chose très simple : cela tient sur une ligne de code. Cependant, avant que vous n'abordiez cette ligne de
code, une digression s'impose pour parler des paquetages.
Le standard Unicode est un mécanisme pour représenter les caractères de tous les différents langages à travers le monde. Quand
Python analyse un document XML, toutes les données sont stockées en mémoire au format Unicode.
Parcourir des documents XML en s'arrêtant à chaque noeud peut être fastidieux. Si vous recherchez un noeud particulier, enfoui au plus profond de votre
document XML, il existe un raccourci pour le retrouver rapidement : getElementsByTagName.
Voilà, c'est tout pour ce qui concerne XML en tant que tel. Le chapitre suivant reprend les mêmes programmes donnés en exemple,
mais en mettant l'accent sur certains aspects qui rendent plus souple leur maniement : l'utilisation des flots de données
(streams) pour le traitement des données en entrée, l'utilisation de getattr pour la sélection de méthode et l'utilisation des drapeaux de ligne de commande pour permettre aux utilisateurs de paramétrer
le programme sans intervenir dans le code.
L'une des grandes forces de Python repose sur son principe de liaison dynamique, dont un puissant usage est le pseudo objet-fichier (file-like objecti).
Les utilisateurs d'UNIX sont déjà familiers avec les concepts d'entrée standard, de sortie standard et d'erreur standard. Cette section s'adresse
aux autres.
kgp.py recourt à différentes astuces qui peuvent ou non se révéler également utiles dans votre traitement XML. La première tire avantage de la structure logique des documents en entrée pour construire un cache de noeuds.
Une autre technique bien utile lorsqu'il s'agit d'analyser un document XML consiste à retrouver tous les descendants directs d'un élément particulier. Par exemple, dans les fichiers de grammaire,
un élément ref peut contenir plusieurs éléments p, qui à leur tour peuvent contenir un certain nombre de choses, y compris d'autres éléments p. Mais vous ne voulez retrouver que les éléments p qui sont les enfants d'un élément ref et non les éléments p qui sont les enfants d'un autre élément p.
Une troisième astuce bien utile au traitement XML implique la séparation de votre code en fonctions logiques, sur la base des types de noeud et des noms d'élément. Les documents
XML analysés sont constitués de divers types de noeud, chacun représenté par un objet Python. La racine d'un document est elle-même représentée par un objet Document. L'objet Document contient alors un ou plusieurs objets Element (pour les balises XML courantes), dont chacun peut contenir d'autres objets Element, Text (pour les fragments de texte), ou Comment (pour les commentaires imbriqués). Python facilite l'écriture d'un sélecteur pour séparer la logique de chaque type de noeud.
Python supporte complètement la création de programmes qui peuvent être lancés en ligne de commande, à l'aide d'arguments et de
drapeaux longs ou cours pour spécifier diverses options. Cela n'est nullement spécifique à XML, mais comme ce script fait grand usage du traitement en ligne de commande, il est très à propos d'y faire ici mention.
Python est accompagné de puissantes bibliothèques pour analyser et manipuler des documents XML. Le module minidom prend un fichier XML et l'analyse en objets Python, fournissant un accès aléatoire à des éléments arbitraires. Ce chapitre montre encore comment Python peut servir à créer un "véritable" script autonome exécutable en ligne de commande, pourvu de drapeaux et d'arguments de
ligne de commande, d'une gestion d'erreur et même de la capacité de récupérer en entrée la redirection du résultat d'un programme
antérieur.
Imaginons que nous souhaitons télécharger une ressource par HTTP, par exemple un fil Atom. Seulement, nous ne voulons pas
le télécharger une seule fois, nous voulons le télécharger toutes les heures, pour obtenir les dernières nouvelles sur le
site qui fournit le fil. Nous allons le faire de la manière la plus simple, puis nous verrons comment faire mieux.
Pour commencer, nous allons activer les fonctionnalités de débogage de la bibliothèque HTTP de Python pour voir tout ce qui est échangé. Cela nous servira tout au long de ce chapitre, au fur et à mesure que nous rajouterons
des fonctionnalités.
La première chose à faire pour améliorer notre client de services Web HTTP est de faire en sorte qu'il s'identifie correctement
avec une chaîne User-Agent. Pour cela, nous devons aller plus loin qu'urllib et plonger dans urllib2.
Maintenant que nous savons comment ajouter des en-têtes HTTP à nos requêtes de services Web, voyons comment prendre en charge
les en-têtes Last-Modified et ETag.
La dernière fonctionnalité importante du protocole HTTP que nous voulons supporter est la compression. Beaucoup de services
Web ont la capacité d'envoyer les données compressées, ce qui qui peut réduire le volume de données envoyées de 60 % ou plus.
C'est particulièrement vrai des services Web XML puisque les données XML se compressent très bien.
Vous utilisez Google, n'est-ce pas ? N'avez-vous jamais souhaité accéder aux résultats de recherches Google par la programmation
? Maintenant, vous pouvez le faire, voici un programme Python qui fait des recherches sur Google.
Le coeur de SOAP est la l'appel de fonction distant. Il existe un certain nombre de serveurs publics SOAP qui fournissent des fonctions simples à titre de démonstration.
Les appels de méthodes locaux sont délégués à la classe SOAPProxy qui les converti de manière transparente en appels de méthodes SOAP distants. Comme nous l'avons vu, c'est un gros travail et SOAPProxy le fait rapidement et de manière transparente. Mais ce que cette classe ne fait pas est de fournir un mode d'introspection
de méthodes.
Comme beaucoup de choses dans le domaine des services Web, WSDL a un longue et tortueuse histoire, pleine de controverses politiques et d'intrigue. Je n'aborderais pas du tout cette histoire,
je la trouve ennuyeuse à pleurer. Il y a eu des normes concurrentes pour remplir ces fonctions, mais WSDL a gagné, donc apprenons à l'utiliser.
Revenons au code d'exemple que nous avons vu au début du chapitre, qui effectue quelque chose de plus intéressant et de plus
utile qu'obtenir la température.
Les services Web SOAP sont très complexes. La spécification est très ambitieuse et tente de répondre à de nombreux cas d'utilisation différents
des services Web. Ce chapitre a abordé quelques uns des cas d'utilisation les plus simples.
Dans les chapitres précédents, nous avons «plongé»
en regardant immédiatement du code et en essayant de le comprendre le
plus vite possible. Maintenant que vous connaissez un peu plus de
Python, nous allons prendre un peu de recul
et regarder ce qui se passe avant que le code
soit écrit.
Maintenant que nous avons défini entièrement le comportement que
nous attendons de nos fonctions de conversion, nous allons faire
quelque chose d’un peu inattendu : nous allons écrire une suite de
tests qui évalue ces fonctions et s’assure qu’elle se comporte comme
nous voulons qu’elles le fassent. Vous avez bien lu, nous allons
écrire du code pour tester du code que nous n’avons pas encore
écrit.
Voici la suite de tests complète de nos fonctions de conversion de
chiffres romains, qui n’ont pas encore été écrites mais le seront dans
roman.py. La manière dont tout ça fonctionne
ensemble n’est pas immédiatement évidente, aucune de ces classes ou
méthodes ne se référencent entre elles. Il y a de bonnes raisons à cela,
comme nous le verrons bientôt.
La partie fondamentale des tests unitaires est la construction
des cas de test individuels. Un cas de test répond à une seule
question à propos du code qu’il teste.
Il ne suffit pas de tester que nos fonctions réussissent
lorsqu’on leur passe des entrées correctes, nous devons aussi tester
qu’elles échouent lorsque les entrées sont incorrectes. Et pas
seulement qu’elles échouent, qu’elles échouent de la manière
prévue.
Il est fréquent qu’une unité de code contiennent un ensemble de
fonctions réciproques, habituellement sous la forme de fonctions de
conversion où l’une converti de A à B et l’autre de B à A. Dans ce
cas, il est utile de créer un test de cohérence pour s’assurer qu’une
conversion de A à B puis de B à A n’introduit pas de perte de
précision décimale, d’erreurs d’arrondi ou d’autres bogues.
Maintenant que nos tests unitaires sont complets, il est temps
d’écrire le code que nos cas de test essaient de tester. Nous allons
faire cela par étapes, de manière à voir tous les cas échouer, puis à
les voir passer un par un au fur et à mesure que nous remplissons les
trous de roman.py.
Maintenant que toRoman se comporte
correctement avec des entrées correctes (des entiers de
1 à 3999), il est temps de faire
en sorte qu’il se comporte bien avec des entrées incorrectes (tout le
reste).
Maintenant que toRoman est terminé, nous
devons passer à fromRoman. Grâce à notre
structure de données élaborée qui fait correspondre les nombres
romains à des valeurs entières, ce n’est pas plus difficile que pour
toRoman.
Maintenant que fromRoman fonctionne pour
des entrées correctes, nous devons mettre en place la dernière pièce
du puzzle : le faire fonctionner avec des entrées incorrectes. Cela
veut dire trouver une manière d’examiner une chaîne et de déterminer
si elle constitue un nombre en chiffres romains valide. C’est intrinsèquement plus
difficile que de valider une entrée
numérique dans toRoman, mais nous avons un
outil puissant à notre disposition : les expressions
régulières.
Malgré tous vos efforts pour écrire des tests unitaires
exhaustifs, vous aurez à faire face à des bogues. Mais qu’est-ce que
je veux dire par «bogue» ? Un bogue est un cas de test
que vous n’avez pas encore écrit.
Malgré vos meilleurs efforts pour plaquer vos clients au sol et
leur extirper une définition de leurs besoins grâce à la menace, les
spécifications vont changer. La plupart des clients ne savent pas ce
qu’ils veulent jusqu’à ce qu’ils le voient et même ceux qui le savent
ne savent pas vraiment comment l’exprimer. Et même ceux qui savent
l’exprimer voudront plus à la version suivante de toute manière.
Préparez-vous donc à mettre à jour vos cas de test à mesure que vos
spécifications changent.
Le meilleur avec des tests unitaires exhaustifs, ce n’est pas le
sentiment que vous avez quand tous vos cas de test finissent par
passer, ni même le sentiment que vous avez quand quelqu’un vous
reproche d’avoir endommagé leur code et que vous pouvez véritablement
prouver que vous ne l’avez pas fait. Le meilleur,
c’est que les tests unitaires vous permettent la refactorisation
continue de votre code.
Un lecteur astucieux a lu la section précédente et l’a amené au
niveau supérieur. Le point le plus compliqué (et pesant le plus sur
les performances) du programme tel qu’il est écrit actuellement est
l’expression régulière, qui est nécessaire puisque nous n’avons pas
d’autre moyen de subdiviser un nombre romain. Mais il n’y a que 5000
nombres romains, pourquoi ne pas construire une table de référence une
fois, puis simplement la lire ? Cette idée est encore meilleure quand
on réalise qu’il n’y a pas besoin d’utiliser les expressions régulière
du tout. Au fur et à mesure que l’on construit la table de référence
pour convertir les entiers en nombres romains, on peut construire la
table de référence inverse pour convertir les nombres romains en
entiers.
Les tests unitaires forment un concept puissant qui, s’il est
implémenté correctement, peut à la fois réduire les coûts de
maintenance et augmenter la flexibilité d’un projet à long terme. Il
faut aussi comprendre que les tests unitaires ne sont pas une panacée,
une baguette magique ou une balle d’argent. Ecrire de bons cas de test
est difficile et les tenir à jour demande de la discipline (surtout
quand les clients réclament à hauts cris la correction de bogues
critiques). Les tests unitaires ne sont pas destinés à remplacer
d’autres formes de tests comme les tests fonctionnels, les tests
d’intégration et les tests utilisateurs. Mais ils sont réalisables et
ils marchent et une fois que vous les aurez vu marcher, vous vous
demanderez comment vous avez pu vous en passer.
Au Chapitre 13, Tests unitaires, vous avez appris la philosophie des
tests unitaires. Au Chapitre 14, Ecriture des tests en premier, vous avez suivi pas à
pas l'implémentation de tests unitaires en
Python. Au Chapitre 15, Refactorisation, vous
avez vu comment les tests unitaires facilitent la refactorisation à
grand échelle. Ce chapitre va poursuivre le développement de ces
programmes, mais cette fois en mettant l'accent sur des techniques
avancées de Python plutôt que sur les test
unitaires proprement dit.
Lorsque vous exécutez des scripts Python depuis la ligne de commande, il est parfois utile de savoir l'emplacement sur le disque du script en cours d'exécution.
Vous êtes déjà familier avec l'utilisation des list
comprehensions pour le filtrage de listes. Il y
a une autre manière de faire la même chose que certaines personnes
trouvent plus expressive.
Vous avez déjà vu comment appliquer les list
comprehensions aux mutations de listes. Il y a
une autre manière d'obtenir la même chose en utilisant la fonction
prédéfinie map. Elle fonctionne de manière
similaire à la fonction filter.
Maintenant, vous vous demandez certainement pourquoi tout ça est
mieux que d'utiliser des boucle for et de simples
appels de fonction. C'est une question tout à fait justifiée. En fait,
c'est avant tout une question de perspective, utiliser
map et filter vous oblige à
centrer votre réflexion sur les données.
Vous en avez assez appris pour déconstruire les sept premières
lignes du code d'exemple de ce chapitre : lire un répertoire et
importer des modules sélectionnés parmi ceux qu'il contient.
Je vais vous parler du pluriel des noms (en anglais). Nous
verrons ensuite les fonctions qui retournent d'autres fonctions, les
expressions régulières avancées et les générateurs. Les générateurs
sont une nouveauté de Python 2.3. Mais
commençons par le pluriel des noms.
Nous avons donc des mots, qui, en anglais du moins, sont
constitués de chaînes de caractères. Par ailleurs, nous avons des
règles qui disent que nous devons reconnaître différentes combinaisons
de caractères, et leur faire subir certaines modifications. C'est un
problème qui semble être fait pour les expressions régulières.
Maintenant, nous allons ajouter un niveau d'abstraction. Nous
avons commencé par définir une liste de règles : si telle condition
est remplie, alors effectuer telle action, sinon passer à la règle
suivante. Nous allons temporairement rendre plus complexe une partie
du programme pour pouvoir en simplifier une autre.
Définir de fonctions séparément pour chaque règle de recherche
et de transformation n'est pas vraiment nécessaire. Nous ne les
appelons jamais séparément, elles sont définies dans la liste de
règles rules et appelées à partir de cette liste.
Nous allons simplifier la définition des règles en rendant ces
fonctions anonymes.
Nous avons extrait toute duplication de code et ajouter assez
d'abstraction pour que les règles de pluriel des noms soient définies
sous forme d'une liste de chaînes. La prochaine étape est logiquement
de mettre ces chaînes dans un fichier séparé, pour qu'elles puissent
être modifiées séparément du code qui les utilise.
La chose la plus importante que vous devez savoir à propos de l'optimisation de code Python est que vous ne devez pas écrire vos propres fonctions de chronométrage.
La première chose que la fonction Soundex vérifie est que l'entrée est une chaîne non-vide composée de lettres. Quelle est
la meilleure manière de faire cela ?
La deuxième étape de l'algorithme Soundex est de convertir les caractères en chiffres suivant des règles précises. Quelle
est la meilleure manière de procéder ?
L'étape finale de l'algorithme Soundex est de compléter les résultats courts par des zéros et de tronquer les résultats long.
Quelle est la meilleure manière de faire cela ?