You are here: Sommaire > Plongez au coeur de Python > Survol en cinq minutes | << >> | ||||
Plongez au coeur de PythonDe débutant à expert |
Chapitre 1. Installation de Python
La première chose à faire avec Python est de l'installer. Mais est-ce nécéssaire ?
Sous Windows, il y a plusieurs possibilités pour installer Python.
Sous Mac OS 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.
Mac OS 9 n'est fournit avec aucune version de Python, mais l'installation en est très simple.
Téléchargez le dernier RPM Python 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 avez la chance d'utiliser Debian GNU/Linux, vous pouvez installer Python à l'aide de la commande apt.
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.
Maintenant que Python est installé, nous allons voir en quoi consiste cette interface interactive que vous avez lancé.
Vous devez maintenant avoir une version de Python installée et fonctionnelle.
Chapitre 2. Votre premier programme Python
Voici un programme Python complet et fonctionnel.
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.
Vous pouvez documenter une fonction Python en lui donnant une chaîne de documentation (doc string).
Une fonction, comme tout le reste en Python, est un objet.
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__.
Un des types de données fondamentaux de Python est le dictionnaire, qui défini une relation 1 à 1 entre des clés et des valeurs.
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.
Un tuple (n-uplet) est une liste non-mutable. Un fois créé, un tuple ne peut en aucune manière être modifié.
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.
A présent, le programme odbchelper.py et sa sortie devraient vous paraître parfaitement clairs.
Chapitre 4. Le pouvoir de l’introspection
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.
Le programme apihelper.py et sa sortie devraient maintenant être parfaitement clairs.
Chapitre 5. Les objets et l'orienté objet
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.
Chapitre 6. Traitement des exceptions et utilisation de fichiers
Comme beaucoup de langages orientés objet, Python gère les exception à l'aide de blocs try...except.
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.
Le programme fileinfo.py, introduit au Chapitre 5; devrait maintenant être parfaitement clair.
Chapitre 7. Expressions régulières
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.
Vous avez certainement déjà vu des chiffres romains, par exemple dans Astérix[2]
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.
Chapitre 8. Traitement du HTML
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.
Il existe une technique de formatage de chaînes alternative utilisant un dictionnaire au lieu de valeurs stockées dans un tuple.
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.
Il est temps d’utiliser tout ce que nous avons appris. J’espère que vous avez été attentif.
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.
Chapitre 9. Traitement de données XML
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.
Comme je le disais, analyser un document XML est chose très simple qui tient en une ligne de code. A vous de décider de la suite.
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.
Les éléments XML peuvent avoir un ou plusieurs attributs et il est très facile d'y accéder une fois le document XML analysé.
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.
Chapitre 10. Des scripts et des flots de données (streams)
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.
Vous avez déjà parcouru un long chemin. Portez votre regard en arrière et voyez comment rassembler toutes ces étapes.
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.
Chapitre 11. Services Web HTTP
Nous avons vu le traitement du HTML et le traitement du XML et, au cours de ces chapitres, comment télécharger une page Web et comment analyser du XML à partir d'une URL. Nous allons maintenant plonger dans le sujet plus général des services Web HTTP.
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.
Il y a cinq fonctionnalités importantes de HTTP que nous devons supporter dans notre programme.
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 prise en charge des redirections temporaires et permanentes se fait avec un autre type de gestionnaire d'URL spécialisé.
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.
Nous avons vu toutes les pièces nécessaires à la construction d'un client de services Web intelligent. Maintenat, voyons comment tout cela s'assemble.
openanything.py et ses fonctions devraient être tout à fait clairs maintenant.
Chapitre 12. Services Web SOAP
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.
Contrairement au reste de ce livre, ce chapitre utilise des bibliothèques qui ne sont pas distribuées avec Python.
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 bibliothèques SOAP fournissent une manière simple de voir ce qu'il se passe dans les coulisses.
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.
Bien sûr, le monde des services Web SOAP n'est pas différent du reste. Parfois ça ne marche pas.
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.
Chapitre 14. Ecriture des tests en premier
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 nous avons la structure de notre module roman en place, il est temps de commencer à écrire du code et à passer les cas de test.
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.
Chapitre 16. Programmation fonctionnelle
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.
Assez de discours philosophiques. Parlons plutôt de l'importation dynamique de modules.
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.
Le programme regression.py et sa sortie doivent maintenant être parfaitement clairs.
Chapitre 17. Fonctions dynamiques
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 allons extraire la duplication de code pour rendre plus facile la définition de nouvelles règles.
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.
Maintenant, vous êtes prêts à une discussion sur les générateurs.
Nous avons vu de nombreuses techniques avancées dans ce chapitre. Ces techniques ne sont pas appropriées dans toutes les situations.
Chapitre 18. Ajustements des performances
Il y a tellement de dangers liés à l'optimisation du code qu'il est difficile de savoir par quoi commencer.
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 ?
La troisième étape de l'algorithme Soundex est l'élimination des doublons successifs. Quelle est la meilleure manière de faire cela ?
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 ?
Ce chapitre a illustré plusieurs aspects importants des réglages de performances en Python et en général.
<< Pour en savoir plus |
| | |
Trucs et astuces >> |