Tutoriel python niveau intermédiaire: CHAÎNES MISE EN FORME
Bienvenue dans le tutoriel python niveau intermédiaire numéro 5.
PROBLÈMES DE FORMATAGE
On écrit en IDLE un cycle simple qui imprime les carrés et les cubes des nombres de 1 à 10 (après avoir écrit la deuxième ligne il faut appuyer deux fois sur pour l’exécuter) :
>>> for i in range(1, 11): print(i, i ** 2, i ** 3) 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000
La sortie de IDLE est assez gênante : à mesure que les nombres augmentent, ils occupent de plus en plus d’espaces, et ils ne sont donc pas empilés. Nous pourrions certainement essayer d’aligner ces chiffres avec des instructions supplémentaires, mais cela entraînerait plusieurs complications.
Voici un autre exemple : Ici nous avons une liste de vendeurs, dont chacun a réalisé un certain nombre de ventes et a droit à une commission (un vendeur est stocké dans un vocabulaire avec prénom, nom, ventes et pourcentage de la commission) . Nous voulons imprimer une liste de vendeurs avec le montant de la commission due à chacun d’eux. Copiez ce programme dans l’éditeur de texte :
sellers = [ {"surname": "Abbotti", "name": "Giovanni", "sales": 36515.35, "commission": 2.35}, {"surname": "Bellini", "name": "Luigi", "sales": 45210.00, "commission": 2.5}, {"surname": "Cardellini", "name": "Matteo", "sales": 28534.50, "commission": 3.12}, {"surname": "Decini", "name": "Piervittorio", "sales": 33782.80, "commission": 2.94} ] for v in sellers: print (v ["surname"], v ["name"], v ["sales"] * v ["commission"] / 100)
Si vous l’exécutez, vous obtiendrez cette sortie :
Abbotti Giovanni 858.110725 Bellini Luigi 1130.25 Cardellini Matteo 890.2764 Decini Piervittorio 993.21432
Ici aussi, l’effet graphique est horrible : noms, prénoms et numéros doivent être imprimés sur trois colonnes différentes ; les nombres doivent alors être alignés sur la virgule et tous avec deux décimales. Cela pourrait également être fait avec des instructions supplémentaires, mais au final, il faudrait probablement plus de temps pour obtenir le résultat correct que pour résoudre le problème du calcul des commissions !
FORMATAGE DES CHAÎNES
Tous les langages de programmation (dont certains remontent à l’époque où la seule sortie possible était l’impression sur papier) proposent des instructions pour bien vérifier ce que vous imprimez (alignement des nombres et des mots, nombre de décimales, etc.). La commodité de la fonction print() de Python signifie que, le plus souvent, il suffit de l’utiliser dans sa forme la plus simple, mais si nous voulons plus de contrôle, Python nous propose également plusieurs outils.
Il faut dire qu’au fil des années et des différentes versions Python a connu une évolution : jusqu’à Python 2 une série d’instructions a été adoptée qui rappelait celles utilisées dans le langage C, à partir de la version 3 en revanche, de nouvelles instructions totalement différentes ont été introduites, qui donnent plus de possibilités de contrôle. Aujourd’hui, les deux systèmes sont appelés “Formatage de style ancien” et “Formatage de nouveau style”. L’ancien style est toujours accepté par Python 3 mais les nouveaux utilisateurs sont encouragés à utiliser le nouveau style. Pour cette raison nous ne parlerons ici que de ces derniers, et même ainsi nous devrons nous limiter uniquement aux fonctionnalités les plus utilisées, car les diverses combinaisons de possibilités sont vraiment nombreuses et constituent un véritable « mini langage » au sein de Python. Après tout, ceux qui connaissent déjà le C ne devraient pas avoir trop de problèmes pour apprendre les instructions à l’ancienne.
LA MÉTHODE DE FORMAT
La base du formatage “nouveau style” en Python est la méthode format (), qui peut être appliquée à une chaîne avec la syntaxe à points habituelle. Nous ouvrons IDLE et écrivons :
>>> "Bonjour maman !". Format () 'Bonjour maman!' >>> s = "Salut Luigi !" >>> au format () 'Bonjour Luigi!'
Notez que nous avons d’abord appliqué la méthode à une constante (c’est-à-dire une chaîne explicitement écrite entre guillemets) puis à une variable contenant une chaîne. IDLE répond comme d’habitude en affichant la valeur renvoyée par la méthode, c’est-à-dire, pour le moment, la chaîne inchangée. Nous allons maintenant voir comment manipuler ce que nous voulons imprimer en utilisant le format ().
Le format () intervient sur la chaîne à laquelle il s’applique en la modifiant de diverses manières. Pour lui permettre de le faire, nous devons d’abord indiquer les parties de la chaîne qu’il doit modifier via des champs de remplacement (dans les champs de remplacement de la documentation officielle, dans la suite nous les appellerons uniquement des champs), en suivant ces règles :
Un champ est composé d’accolades ouvertes et fermées, insérées dans la chaîne à la place du texte à formater (si on veut écrire une accolade il faut l’écrire deux fois : “{{” ou “}}”).
Chaque champ doit correspondre à un paramètre au format (), qui indique le texte à remplacer à la place du champ correspondant
Voici un exemple pour mieux comprendre :
>>> s = "Bonjour {} !" # chaîne contenant un champ de remplacement >>> s.format ("Luigi") # remplace "Luigi" par le champ 'Bonjour Luigi!' >>> au format ("Sandra") # remplace "Sandra" "Bonjour Sandra" >>> un = 10 >>> b = 20 >>> "{} x {} = {}". format (a, b, a * b) # trois champs, trois paramètres '10 x 20 = 200 ' >>> "{{{}}}". format (a) # comment écrire les accolades '{dix}'
Dans la première instruction, nous avons défini une chaîne s contenant un champ de remplacement (les deux accolades). Dans les deuxième et troisième, nous avons utilisé le format () pour remplacer d’abord “Luigi” puis “Sandra” au lieu des crochets. Passons maintenant aux autres instructions : on affecte d’abord une valeur à deux variables a et b, puis on les imprime (avec leur produit) en utilisant un format (). Enfin, dans la dernière instruction, on imprime une variable entre accolades : on doit en utiliser trois à la suite (deux pour la parenthèse et une pour le champ).
En écrivant les parenthèses ouvertes et immédiatement fermées, les paramètres du format () seront affectés aux champs selon leur ordre, mais on peut aussi contourner cette règle en indiquant la correspondance entre champs et paramètres. Là aussi nous avons plusieurs options (je vous l’ai dit, c’est un vrai mini-langage, si cela vous semble trop compliqué vous pouvez aussi sauter cette partie et la reprendre quand vous aurez une idée plus précise).
Nous pouvons indiquer le paramètre avec sa position, en écrivant un nombre à l’intérieur des accolades (comme d’habitude, les positions commencent à 0).
>>> "{0} et {1} sont mes amis" .format ("Luigi", "Sandra") "Luigi et Sandra sont mes amis" >>> "{1} et {0} sont mes amis" .format ("Luigi", "Sandra") "Sandra et Luigi sont mes amis" >>> "{0} {1} {0} {0} {1}". Format ("Luigi", "Sandra") 'Luigi Sandra Luigi Luigi Sandra'
Ou nous pouvons donner un nom au paramètre, en attribuant ce nom au format () comme argument mot-clé (si vous ne vous souvenez pas de ce qu’ils sont, regardez ici. Attention ! Les noms donnés entre accolades ne sont valides qu’au format chaîne paire et nous ne pouvons pas utiliser de variables que nous avons définies en externe.
>>> "J'aime {ami}". Format (ami = "Luigi") "J'aime Louis" >>> ami = "Sandra" >>> "J'aime {ami}". Format () # cela NE PEUT PAS être fait Traceback (dernier appel le plus récent) : Fichier "", ligne 1, dans "J'aime {ami}". Format () KeyError : 'ami'
la deuxième tentative provoque une erreur, car le nom de l’ami doit être défini à l’intérieur du format ().
À ce stade, vous êtes probablement nombreux à penser : mais jusqu’à présent, il n’y a rien de nouveau ! Nous venons d’apprendre une façon plus compliquée de faire les choses que nous connaissions déjà ! C’est vrai, mais à partir de maintenant, nous commençons à voir d’autres règles qui nous permettent de modifier plus radicalement la chaîne à laquelle le format () est appliqué. Dans un champ de remplacement, nous pouvons insérer d’autres caractères de spécification de format qui contrôlent la largeur d’impression, l’alignement, etc.
MISE EN FORME DU TEXTE
Commençons par la mise en forme du texte : les spécificateurs de format doivent commencer par deux-points ‘:’ et doivent suivre cette syntaxe (un nom entre crochets indique qu’il est facultatif) :
: [caractère de remplissage] [caractère d’alignement] [largeur du champ] [troncature .]
- Les deux-points sont obligatoires et servent à séparer les caractères de formatage du reste du spécificateur (c’est-à-dire du nom ou du numéro éventuel du paramètre que nous avons vu au paragraphe précédent).
- Le caractère de remplissage (facultatif) sera utilisé pour remplir les espaces ajoutés au texte. Si aucun format () n’est donné, utilisez un espace blanc normal.
- Le caractère d’alignement (facultatif) peut être l’un des trois suivants :
caratère | Signification |
< | Texte aligné à gauche (c’est le comportement par défaut si le caractère d’alignement est omis) |
^ | Texte centré |
> | Texte aligné à droite |
- La largeur du champ est un nombre entier qui spécifie le nombre total de caractères à imprimer. Il est également facultatif (dans le sens où l’omettre n’entraîne pas d’erreur) mais s’il est omis, il imprime le nombre exact de caractères du texte attribué, rendant les autres spécifications inutiles.
- Si le nombre de caractères de notre texte est supérieur à celui indiqué dans le bloc largeur, le comportement par défaut est de ne pas tronquer le texte, dépassant ainsi le nombre de caractères indiqués. Si au contraire nous voulons la troncature, nous pouvons l’indiquer en plaçant un point et un autre chiffre après la largeur, ce qui indique le nombre maximum de caractères (en partant de la gauche) à imprimer, après quoi le texte est tronqué.
Essayons de mieux comprendre avec quelques exemples :
>>> "{:> 20}". Format ("Luigi") # largeur 20, aligné à droite 'Luigi' >>> "{: <20}". Format ("Luigi") # largeur 20, aligné à gauche 'Luigi' >>> "{: 20}". Format ("Luigi") # le texte est aligné à gauche par défaut 'Luigi' >>> "{: _ ^ 20}". Format ("Luigi") # centré et rempli '_______ Louis________' >>> "{: 20}". Format ("Precipitevolissimmente") # chaîne longue sans troncature 'Précipitevolissimamente' >>> "{: 20.20}". Format ("Precipitevolissimmente") # chaîne longue avec troncature « Précipitevolissimevo » >>> # arguments de mots-clés (allez AVANT les deux-points) >>> "{nom :> 15} {nom :> 15}". format (nom = "Luigi", nom = "Rossi") "Luigi Rossi"
N’oubliez pas de suivre l’ordre à la lettre, sinon vous obtiendrez une erreur :
>>> "{: 10>}". Format ("Luigi") # le> va AVANT 10 Traceback (dernier appel le plus récent) : Fichier "", ligne 1, dans "{: 10>}".Format ("Luigi") ValueError : code de format inconnu '>' pour l'objet de type 'str'
Ces règles nous permettent déjà d’améliorer considérablement notre programme vendor.py. Modifiez les dernières lignes comme ceci :
. . . pour v chez les vendeurs : s = "{: 15} {: 15} {}". format (v ["nom"], v ["nom"], v ["ventes"] * \ v ["commission"] / 100) impression(s)
qui nous donnera enfin les noms des vendeurs en colonnes :
Abbotti Giovanni 858.110725 Bellini Luigi 1130.25 Cardellini Matteo 890.2764 Decini Piervittorio 993.21432
EXERCICE 3.1 Changer la ligne qui définit la chaîne s : aligner les noms à droite ou au centre, utiliser l’astérisque “*” comme caractère de remplissage, changer la largeur des champs
EXERCICE 3.2 Essayez maintenant d’obtenir ce format :
EXERCICE 3.1 Changer la ligne qui définit la chaîne s : aligner les noms à droite ou au centre, utiliser l'astérisque "*" comme caractère de remplissage, changer la largeur des champs EXERCICE 3.2 Essayez maintenant d'obtenir ce format :
ASTUCE : vous devez imprimer uniquement la première lettre du nom… le point après l’initiale doit plutôt être inséré dans la chaîne à laquelle le format est appliqué, après le champ de remplacement. Laissez trois espaces entre l’initiale et le montant de la vente
FORMAT DES NUMÉROS
Pour le formatage des nombres, les choses se compliquent un peu, car Python nous propose de nombreuses options : on peut contrôler le nombre de décimales, la base (décimale, binaire, hexadécimale…), la notation exponentielle… ; dans la suite je vais essayer d’être le plus complet possible, mais il va sans dire que vous n’aurez probablement jamais besoin de toutes ces options, il vous suffit donc de comprendre les principaux exemples ; si à l’avenir vous avez besoin d’imprimer des nombres sous forme hexadécimale, recherchez simplement le code spécifique dans une référence. Voici le format complet des spécificateurs :
: [[remplir] alignement] [signe] [#] [0] [largeur] [,] [.précision] [type]
Encore une fois, le spécificateur doit commencer par deux-points, suivi (dans l’ordre exact dans lequel ils sont indiqués) par les différents blocs, qui sont tous facultatifs. Analysons-les un par un (par souci de clarté je ne les énumérerai pas dans le même ordre d’apparition) :
- [width] Il a la même signification déjà vue : le nombre de caractères de la chaîne en sortie. Un nombre n’est jamais tronqué : s’il faut plus de caractères, tous ceux nécessaires sont imprimés, sinon c’est le caractère de remplissage qui est utilisé.
- [[fill] alignement] Similaire également à ce que nous avons vu dans la mise en forme du texte, pour les nombres il y a cependant quelques différences pour le caractère d’alignement :
caractère | Signification | Exemple |
< | Numéro aligné à gauche | >>> "{:<15}".format(-10) '-10 ' |
^ | Numéro centré | >>> "{:^15}".format(-10) ' -10 ' |
> | Nombre aligné à droite (c’est la valeur par défaut pour les nombres, notez que le texte est aligné à gauche par défaut) | >>> "{:>15}".format(-10) ' -10' >>> "{:15}".format(-10) # lo stesso ' -10' |
= | Le caractère de remplissage est placé entre le signe et le nombre. | >>> "{:=15}".format(-10) '- 10' |
[signe] Il peut s’agir de l’un de ces trois caractères :
caratère | Signification | Exemple |
+ | Le signe est toujours placé avant les nombres positifs et négatifs | >>> "{:+}".format(10) '+10' >>> "{:+}".format(-10) '-10' |
– | Le signe est préfixé uniquement aux nombres négatifs. C’est le comportement par défaut si le caractère est omis | >>> "{:-}".format(10) '10' >>> "{:-}".format(-10) '-10' |
espace | Un signe moins est préfixé pour les nombres négatifs et un espace pour les nombres positifs | >>> "{: }".format(10) ' 10' >>> "{: }".format(-10) '-10' |
- [0] Il est généralement utilisé comme alternative à fill.alignment dans le cas le plus courant du zéro-fill. S’il est présent, 0 est utilisé comme caractère de remplissage et tout signe est précédé de zéros, sans qu’il soit nécessaire d’utiliser le caractère “=” déjà vu ci-dessus.
>>> "{: 10}". Format ("- 123") # sans zéros de tête '-123' >>> "{: 010}". Format ("- 123") # avec des zéros non significatifs '-000000123'
- [,] Si présent, utilisez la virgule pour séparer les milliers, telle qu’utilisée dans la notation anglo-saxonne (pour nous italiens cela pourrait créer de la confusion…).
>>> "{:,}".format(1234567.89) '1,234,567.89'
- [.precisione] un point suivi d’un nombre qui indique le nombre de chiffres décimaux, c’est-à-dire ceux après le point (la virgule en notation italienne). Ce bloc se comporte différemment selon le type de format indiqué (voir ci-dessous pour plus de détails).
- [type] Un caractère qui contrôle la conversion du nombre en différents formats (dans différentes bases ou sous forme exponentielle). Certains de ces types ne peuvent être appliqués qu’aux entiers, tandis que d’autres conviennent à la fois aux entiers et aux flottants (lorsqu’ils sont appliqués à un entier, Python transforme le nombre en flottant puis le formate).
- Les types suivants ne peuvent être appliqués qu’aux entiers et provoqueront une erreur lorsqu’ils seront appliqués à un flottant. De plus, pour eux il n’est pas possible d’indiquer le bloc .precision (ce qui provoque également une erreur) :
caratère | Signification | Exemple |
b | Imprimer le nombre au format binaire (c’est-à-dire en base 2) | >>> "{:b}".format(125) '1111101' |
c | Imprimer le caractère unicode correspondant au numéro | >>> "{:c}".format(125) '}' |
d ou n | Imprimer le nombre au format décimal. C’est le format par défaut si le caractère est omis et que le nombre à imprimer est un entier. | >>> "{:d}".format(125) '125' |
o | Imprimer le nombre au format octal (c’est-à-dire en base 8) | >>> "{:o}".format(17) '21' |
x | Imprimez le nombre au format hexadécimal (c’est-à-dire en base 16); pour les chiffres 10 à 15, utilisez les lettres minuscules a … f. | “{:x}”.format(16253) ‘3f7d’ |
X | Imprimez le nombre au format hexadécimal (c’est-à-dire en base 16); pour les chiffres 10 à 15, utilisez les majuscules A … F. | >>> "{:X}".format(16253) '3F7D' |
Les types suivants s’appliquent à la fois aux entiers et aux flottants. Pour eux, le bloc .precisione est facultatif (s’il est omis, les chiffres décimaux sont 6 de deafult):
caratère | Signification | Exemple |
Imprime le nombre en notation scientifique exponentielle (une partie entière entre 1 et 9, suivie de décimales, suivie du « e » et de la puissance de 10). Le nombre de chiffres décimaux correspond à celui indiqué dans le bloc Précision : si le nombre a moins de chiffres décimaux il ajoute des zéros, s’il en a plus il l’arrondit | >>> "{:e}".format(12.3) '1.230000e+01' >>> "{:.2e}".format(123456) '1.23e+05' | |
Comme le précédent, mais utilisez un E majuscule pour indiquer l’exposant | >>> "{:.4e}".format(12.3) '1.2300E+01' | |
Imprime le nombre avec un nombre fixe de décimales (indiqué par le bloc .precision). Si le nombre a moins de décimales, il ajoute des zéros, s’il en a plus, il l’arrondit | >>> "{:f}".format(12.3) '12.300000' >>> "{:.2f}".format(12.3678) '12.37' | |
Indique le format général : cela permet à Python de choisir de manière autonome s’il faut imprimer le nombre en notation décimale ou exponentielle et le nombre de chiffres décimaux à indiquer, en fonction du contenu des blocs width et .precision. Pour ce format, le bloc .precision ne représente pas le nombre de chiffres décimaux, mais le nombre maximum de chiffres (autres que les zéros de début ou de fin) à imprimer. Il s’agit du format par défaut si le caractère n’est pas spécifié et que le nombre est un flottant. | >>> >>> "{:.8g}".format(123.4) '123.4' >>> >>> "{:.3g}".format(123.4) '123' >>> >>> "{:.2g}".format(123.4) '1.2e+02' | |
Imprimer le nombre en pourcentage, c’est-à-dire multiplié par 100 et suivi du caractère ‘%’ | >>> "{:.2%}".format(0.123) '12.30%' |
[#] S’il est présent, le numéro est formaté en “forme alternative”. Ce signe quelque peu obscur n’affecte que certains des types vus ci-dessus : pour les formats b, o, x, X commence la représentation du nombre respectivement par “0b”, 0o “,” 0x “et” 0X ” (comme il est souvent utilisé dans livres d’informatique), pour le format f, il provoque l’écriture du point à la fin du nombre même lorsque le nombre de décimales est 0.
Terminé le long de cette liste d’options, vous pouvez essayer de faire quelques exercices :
EXERCICE 4.2 Copiez l’instruction suivante dans IDLE (qui imprime 25)
>>> "{:}".format(25)
Changez maintenant le spécificateur entre accolades pour obtenir ces sorties (rappelez-vous toujours que si vous devez appliquer plusieurs modificateurs, suivez exactement l’ordre du motif donné, sinon vous obtiendrez des erreurs) :
‘0025’
‘+0025’
‘** + 25’
‘+ ** 25’
’25 .00 ‘
‘+ 2.50e01’
‘+ 2.50E01’ (un espace avant le nombre)
‘11001’ (trois espaces avant le numéro)
‘11001’ (trois espaces plus tard)
‘0x19’
EXERCICE 4.3 Reprenons notre exemple initial :
>>> for i in range(1, 11): print(i, i ** 2, i ** 3)
et modifiez l’instruction print() pour obtenir cette sortie :
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
ASTUCE : Vous devez remplacer les trois arguments de print() par un seul argument : une chaîne qui contient trois champs de remplacement formatés avec le format (). Remarquez la dernière ligne : il n’y a qu’un seul espace entre 10 100 1000
EXERCICE 4.3 Enfin, nous pouvons également aligner les nombres dans notre fournisseur program.py : modifiez-le pour obtenir cette sortie :
Abbotti Giovanni 858.11
Bellini Luigi 1130.25
Cardellini Matteo 890.28
Decini Piervittorio 993.21