Chaînes formatées classiques

\(\newcommand{\ds}{\displaystyle}\) \(\newcommand{\Frac}{\ds\frac}\) \(\renewcommand{\r}{\mathbb{ R}}\) \(\newcommand{\C}{\mathbb{ C}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\z}{\mathbb{ Z}}\) \(\newcommand{\Q}{\mathbb{ Q}}\) \(\newcommand{\N}{\mathbb{ N}}\) \(\newcommand{\n}{\mathbb{ N}}\) \(\newcommand{\ol}{\overline}\) \(\newcommand{\abs}[1]{\left| \,{#1} \right|}\) \(\newcommand{\pv}{\;;\;}\) \(\newcommand{\ens}[1]{\left\{ {#1} \right\}}\) \(\newcommand{\mens}[1]{\setminus\left\{ {#1} \right\}}\) \(\newcommand{\Par}[1]{\left({#1}\right)}\)

Chaînes formatées classiques

L’essentiel

Les chaînes formatées classiques

La méthode historique de formatage de chaînes en Python (qualifiée de méthode « classique » dans ce document) héritée du C et disponible en Java, reste utilisable mais la méthode format lui est explicitement préférée dans la documentation officielle. En outre, depuis l’apparition des f-chaînes, la méthode classique présente moins d’avantages en concision ou lisibilité.

Présentons justement la méthode classique. Soient les chaînes formatées suivantes :

Son nom est titi et il a 8 ans.
Son nom est Arthur et il a 30 ans.
Son nom est Alfred et il a 100 ans.

Voici le code Python permettant de produire les chaînes formatées ci-dessus :

1
2
3
4
5
6
7
8
9
modele = "Son nom est %s et il a %s ans."

a = modele %("titi", 8)
print(a)

b = modele %("Arthur", 30)
print(b)

print("Son nom est %s et il a %s ans." %("Alfred", 100))
10
11
12
Son nom est titi et il a 8 ans.
Son nom est Arthur et il a 30 ans.
Son nom est Alfred et il a 100 ans.
  • Ligne 1 : le gabarit. C’est une chaîne Python. Les zones à remplacer sont marquées dans le gabarit par %s ; dans l’exemple, il y a deux zones de remplacement dans le gabarit.
  • Ligne 3 : construction de la chaîne formatée (qui est affichée ligne 10). La syntaxe %("titi", 8) indique ce par quoi les zones de la forme %s vont être remplacées dans le gabarit. Le premier « %s » dans le gabarit est remplacé par le premier élément après la parenthèse. De même pour le deuxième %s.
  • Lignes 9 et 12 : noter que l’on n’est pas obligé de placer la gabarit dans une variable.

Description d’une chaîne formatée

On va décrire la syntaxe d’une chaîne formatée en se basant sur l’exemple suivant :

1
2
s = "Son nom est %s et il a %s ans." %("Alfred", 100)
print(s)
3
Son nom est Alfred et il a 100 ans.

L’expression complète

"Son nom est %s et il a %s ans." %("Alfred", 100)

est une chaîne formatée.

Elle se décompose en trois parties, de gauche à droite :

  • le format
  • l’opérateur %
  • le tuple de remplacement

Le format

Le format se présente comme une simple chaîne Python. Dans l’exemple, il s’agit de la chaîne littérale

1
"Je m'appelle %s et j'ai %s ans"

Chaque valeur à remplacer dans cette chaîne (dans l’exemple le nom et l’âge) est signalée par le symbole % suivi d’une lettre particulière (ici s).

Le format représente un modèle (un gabarit) dans lequel seront effectuées des substitutions.

L’opérateur %

L’opérateur % se place après le format et avant l’objet contenant les valeurs de remplacement.

Cet opérateur est l’opérateur modulo de division entière mais dans ce contexte on l’appelle plutôt l’opérateur de formatage de chaîne, parfois aussi connu sous le nom d’opérateur d”interpolation de chaîne.

L’opérateur de formatage % est un opérateur comme un autre, au même titre que + ou *. Il peut être entouré d’autant d’espaces avant et après :

1
2
s = "Son nom est %s et il a %s ans."    %       ("Alfred", 100)
print(s)
3
Son nom est Alfred et il a 100 ans.
  • Ligne 1: on a placé des espaces avant et après l’opérateur %

Double usage du symbole %

On notera donc que le symbole % s’utilise dans des contextes différents :

  • dans le format pour marquer le début des emplacements à remplacer ;
  • dans la chaîne formatée pour signaler l’objet contenant les valeurs de remplacement correspondantes.

Dans le format, % est juste un marqueur interne et dans le deuxième cas, % est un opérateur du langage Python.

Spécificateur de formatage

Les endroits de remplacement dans le format sont signalés par le symbole % suivi d’un ou plusieurs caractères précisant comment le remplacement doit être fait.

Dans l’exemple ci-dessus, il s’agissait de %s. Ces groupes placés dans le format et commençant par le symbole % sont appelés des spécificateurs de formatage. Ils peuvent être plus complexes que ceux de l’exemple, comme %(toto) 010.2f.

Type de conversion

Un spécificateur de formatage commence par % et se termine par une lettre appelée le type de conversion.

La lettre indique quelle conversion la valeur de remplacement doit subir avant le remplacement. Par exemple, la lettre s fait référence au type string (chaîne), la lettre f fait référence au type float (flottant).

Dans l’exemple

1
s = "Son nom est %s et il a %s ans." %("Alfred", 100)

le deuxième spécificateur %s dans le format précise que la valeur de remplacement, ici 100, doit être convertie en une chaîne, plus précisément, la chaîne qui représente l’entier 100 en base 10.

Pour un autre type de conversion, le formatage aurait pu être différent. Par exemple, au lieu du type s, on peut utiliser un type f qui convertit la valeur en flottant :

1
2
s = "Son nom est %s et il a %f ans" %("Alfred", 100)
print(s)
3
Son nom est Alfred et il a 100.000000 ans
  • L’entier 100 est converti en flottant avant remplacement
  • Le remplacement se fait suivant un formatage flottant (séparateur décimal et 6 chiffres derrière).

Le formatage en string (avec s donc) convient à beaucoup d’utilisations usuelles.

Valeurs de remplacement

Dans l’exemple, les valeurs de remplacement sont au nombre de deux : la chaîne Alfred et l’entier 100. Les valeurs de remplacement apparaissent séparées par des virgules et entourées de parenthèses. La paire de parenthèses représente en fait un tuple. Le tuple qui suit l’opérateur % doit contenir autant de valeurs qu’il y a de spécificateurs de formatage présents dans le format.

Les valeurs de remplacement doivent figurer dans un tuple et pas dans une liste.

Tuple de remplacement

Soit le code suivant :

1
2
3
4
gabarit = "Son nom est %s et il a %s ans."

a = gabarit %("titi", 8)
print(a)
5
Son nom est titi et il a 8 ans.

Les valeurs de remplacement dans le gabarit apparaissent après l’opérateur % et sont placées dans un tuple. Ainsi le code peut-être récrit :

1
2
3
4
gabarit = "Son nom est %s et il a %s ans."
t = ("titi", 8)
a = gabarit %t
print(a)

Il est toutefois rare que l’on procède ainsi en pratique.

Cas d’une unique valeur de remplacement

Lorsqu’il y a une seule valeur à remplacer, la syntaxe d’une chaîne formatée peut être simplifiée en ignorant la notation de tuple pour le remplacement :

1
2
3
4
5
6
7
8
print("Son nom est %s !!!" %"toto")

x = "titi"
print("Son nom est %s !!!" %x)

gabarit = "Son nom est %s !!!"
x = "titi"
print(gabarit %x)
 9
10
11
Son nom est toto !!!
Son nom est titi !!!
Son nom est titi !!!
  • 3 variantes de la même syntaxe sont présentées ; dans chaque cas, un seul remplacement est fait et donc l’opérateur % n’est pas suivi d’une parenthèse.

Priorité de l’opérateur de remplacement

Observons le code suivant au comportement inattendu :

1
2
print("Toto a %s ans" %20)
print("Toto a %s ans" %10*2)
3
4
Toto a 20 ans
Toto a 10 ansToto a 10 ans
  • Ligne 1: %s a été remplacé par 10
  • Ligne 2 : bien que 10 * 2 vaille 20, %s n’a pas été remplacé par 20.

Lorsque la valeur de remplacement fait l’objet d’une opération (ici une multiplication), il peut y avoir un conflit de priorité ou d’association avec l’opérateur % de formatage.

Dans l’exemple ci-dessus, le comportement inattendu est en fait équivalent à :

1
2
s = "Toto a %s ans" %10
print(s*2)
3
Toto a 10 ansToto a 10 ans

Par précaution dans ce type de situation, on entourera de parenthèses la valeur à remplacer :

1
print("Toto a %s ans" %(10*2))
2
Toto a 20 ans

Priorité de %

La priorité de l’opérateur % de formatatge de chaîne est la même que celle de l’opérateur arithmétique %.

Les opérateurs % et * ayant même priorité et la syntaxe du langage Python prévoyant que ces opérateurs s’associent de la gauche vers la droite, il en résulte que la chaîne formatée mon_format % v * 2 s’interprète (mon_format % v) * 2.

En fonction des situations, on placera ou non des parenthèses pour changer les priorités. Voici quelques exemples :

1
2
3
4
5
6
7
8
t = ("mardi", 19)
u = ("janvier", 2038)

z = "%s %s %s %s"%(t+u)
print(z)

z = "%s %s "%t + "%s %s"%u
print(z)
 9
10
mardi 19 janvier 2038
mardi 19 janvier 2038

Autre exemple :

1
2
3
4
5
x = 10
y = 42
z= "température : %s" %-max(x, y)

print(z)
6
température : -42

Options de formatage

Un formatage avec une chaîne formatée peut être affiné à l’aide de nombreuses options. Ces options sont plus ou moins utiles et plus ou moins complexes.

Pour les plus importantes, elles permettent, par exemple, de

  • placer la valeur à formater dans un « champ » avec des espaces blancs à gauche ou à droite de la valeur,
  • placer des zéros avant un nombre entier ou décimal,
  • pour un nombre flottant, contrôler le nombre de décimales à placer dans la chaîne de remplacement

Dans le but de montrer la syntaxe des options, voici un exemple artificiellement complexe de chaîne formatée avec des options :

1
2
3
dico = {"nom":"Alfred", "age": 100}
s = "Son nom est %(nom)s et il a %(age)0 8.3f ans" %dico
print(s)
4
Son nom est Alfred et il a  100.000 ans

Les options de formatage se lisent (dans l’exemple, ligne 2) dans les spécificateurs de formatage entre le signe % et la dernière lettre du spécificateur (et qui désigne le type de conversion). Dans l’exemple, le spécificateur de formatage %(age)0 8.3f contient un certain nombre d’options (5 très précisément), que l’on peut extraire de la suite (age)0 8.3

Les spécificateurs %s, %d, %f

Le caractère de conversion dans un spécificateur (comme s, d ou f) indique comment la valeur de remplacement va être interprétée. Les caractères de conversion les plus courants sont :

  • d : la valeur est interprétée comme un entier sous forme décimale (d comme decimal)
  • s : la valeur est interprétée comme une chaîne de caractères (s comme string)
  • f : la valeur est interprétée comme un nombre décimal à virgule (f comme float)

Voici quelques exemples :

1
2
x = 3.1415926
print("pi vaut %f environ" %x)
3
pi vaut 3.141593 environ
  • x est est un flottant qui doit être interprété comme un flottant, d’où l’affichage
1
2
x = 3.1415926
print("pi vaut %d environ" %x)
3
pi vaut 3 environ
  • x est un flottant mais il va être interprété comme un entier d’où l’affichage 3 (nombre entier, la partie décimale est supprimée).

Formatage d’un flottant avec %f

On cherche à générer le format suivant

1 euro = XXXX dollar

où XXXX est le cours du dollar, par exemple

1 euro = 1,3142 dollar
1 euro = 1,53 dollar
1 euro = 1,279 dollar

XXXX représente donc un nombre flottant.

La syntaxe Python propose le spécificateur %f pour représenter dans une chaîne formatée les nombres décimaux à virgule :

1
2
3
gabarit = "1 euro = %f dollar"
s = gabarit %1.3142
print(s)
4
1 euro = 1.314200 dollar

Ici, le f dans %f fait référence à flottant.

On observe (ligne 4) que le formatage de la valeur est différent de celui de la valeur entrée. La raison est que la précision par défaut du formatage est de 6 décimales.

Problème d’arrondi

Sans option particulière, le spécificateur %f formate en 6 décimales par défaut ce qui affiche parfois un arrondi :

1
2
x = 0.0000009563
print("%f" %x)
3
0.000001

Formatage d’un nombre avec %s

Le spécificateur %s convertit un nombre x en une suite de caractères représentant x en base 10.

Le cas des entiers

Pour les entiers, %s et %d sont équivalents :

1
2
3
4
5
fs = "Mardi %s janvier %s" %(19, 2038)
fd = "Mardi %d janvier %d" %(19, 2038)
print(fs)
print(fd)
print(fs == fd)
6
7
8
Mardi 19 janvier 2038
Mardi 19 janvier 2038
True

Le cas des flottants

Pour les flottants, %s et %f sont proches mais différent pour des raisons d’ajout de zéros :

1
2
3
4
5
fs = "1 euro = %s dollar" %1.3142
ff = "1 euro = %f dollar" %1.3142

print(fs)
print(ff)
6
7
1 euro = 1.3142 dollar
1 euro = 1.314200 dollar
  • Dans le cas de %f, les décimales ont été complétées par des zéros.

Comme le format %f est limité, par défaut, à 6 décimales visibles, les représentations formatées par %s et %f peuvent différer :

1
2
3
4
x = 0.1234678

print("%f" %x)
print("%s" %x)
5
6
0.123468
0.1234678

Précision d’affichage d’un flottant

Un nombre décimal peut être formaté avec un nombre de décimales que l’on peut choisir :

1
2
x = 22/7
print("pi = %.2f" % x)
3
pi = 3.14

Pour cela, on place un point après le signe % et on indique la précision souhaitée (ci-dessus 2), c’est-à-dire le nombre de décimales.

La valeur remplacée est un arrondi de la valeur réelle et non pas une troncature :

1
2
3
x = 3.1415926
print("pi = %.2f" % x)
print("pi = %.4f" % x)
4
5
pi = 3.14
pi = 3.1416

Symbole % dans une chaîne formatée

Supposons que l’on veuille générer des chaînes formatées du type

Inflation : XXXXX% par an

où XXXXX est un entier, par exemple :

1
2
3
Inflation : 10% par an
Inflation : 3% par an
Inflation : 8% par an

La présence du symbole % pose un problème de formatage puisque % est à la fois un signe de remplacement et le caractère littéral de pourcentage :

1
2
p = 10
print("Inflation : %d% par an" %p)
3
4
5
6
Traceback (most recent call last):
  File "_.py", line 2, in <module>
    print("Inflation : %d% par an" %p)
TypeError: not enough arguments for format string
  • Le deuxième % dans le format est interprété comme le début d’un spécificateur de formatage alors que c’est juste le symbole littéral du pourcentage.

Pour faire reconnaître le signe %, la syntaxe des chaînes formatées est %% :

1
2
p = 10
print("Inflation : %d%% par an" %p)
3
Inflation : 10% par an
  • Ligne 2 : Pour lire sans ambiguïté le signe %, la syntaxe Python exige qu’on fasse apparaître % sous la forme %% dans la chaîne formatée.

Alternative

On aurait pu aussi résoudre le problème de la manière suivante

1
2
s = "Inflation : %d%s par an" %(10, "%")
print(s)
3
Inflation : 10% par an

Échappement

Ainsi, le caractère % joue un rôle d’échappement de lui même. Noter que le caractère \

d’échappement habituel ne fonctionne pas :

1
2
p = 10
print("Inflation : %d\% par an" %p)
3
4
    print("Inflation : %d\% par an" %p)
TypeError: not enough arguments for format string

Formatage de chaînes et dictionnaire

Dans une chaîne formatée, après l’opérateur %, on trouve un conteneur où se trouvent les valeurs de remplacement. Le cas déjà rencontré est celui d’un conteneur qui est un tuple.

Un dictionnaire dont les clés sont des chaînes de caractères peut aussi être utilisé pour formater des chaînes :

1
2
3
4
5
6
7
gabarit = "Le langage %(langage)s existe depuis %(date)d"

dico1 ={'langage': 'Python', "date":1991}
print(gabarit %dico1)

dico2 ={'langage': 'C', "date":1972, "auteur": "Ritchie"}
print(gabarit %dico2)
8
9
Le langage Python existe depuis 1991
Le langage C existe depuis 1972

Ici, on dispose du gabarit :

Le langage NOM existe depuis DATE

NOM est à remplacer par une chaîne et DATE par un entier.

La chaîne formatée correspondant à ce gabarit est le format suivant :

"Le langage %(langage)s existe depuis %(date)d"

On observe que dans le format, les marqueurs % sont suivis de parenthèses contenant une chaîne, ici langage et date. Le format s’utilise en écrivant

format %dico

dico est un dictionnaire contenant au moins une clé "langage" et une clé "date" dont les noms correspondent aux noms entre les parenthèses dans les spécificateur de formatage.

Chaque valeur à remplacer dans format est signalée par un marqueur % suivi

  • de la clé du dictionnaire dont la valeur va servir de remplacement
  • d’une lettre (ici s et d) qui indique la conversion effectuée, ici s pour string et d pour entier décimal.

Comparaison avec d’autres styles de formatage

Malgré l’ancienneté du format, le format précédent est peut-être le plus lisible des formats disponibles. Comparons.

D’abord le formatage classique que l’onvient de voir :

1
2
3
4
5
6
7
dico1 ={'langage': 'Python', "date":1991}
dico2 ={'langage': 'C', "date":1972, "auteur": "Ritchie"}

gabarit = "Le langage %(langage)s existe depuis %(date)d"

print(gabarit %dico1)
print(gabarit %dico2)

Le formatage avec la méthode format :

1
2
3
4
5
6
7
dico1 ={'langage': 'Python', "date":1991}
dico2 ={'langage': 'C', "date":1972, "auteur": "Ritchie"}

gabarit = "Le langage {0[langage]} existe depuis {0[date]}"

print(gabarit.format(dico1))
print(gabarit.format(dico2))

Le formatage avec des f-chaînes :

1
2
3
4
5
6
7
8
dico1 ={'langage': 'Python', "date":1991}
dico2 ={'langage': 'C', "date":1972, "auteur": "Ritchie"}

f1 = f"Le langage {dico1['langage']} existe depuis {dico1['date']}"
f2 = f"Le langage {dico2['langage']} existe depuis {dico2['date']}"

print(f1)
print(f2)

Champ

Notion de champ

L’exemple ci-dessous illustre la notion de champ dans un formatage :

1
2
s = "xxxxxxx%5dxxxxxxxxx" %42
print(s)
3
xxxxxxx   42xxxxxxxxx

On peut placer la valeur de remplacement (ici 42) à l’intérieur d’un champ qui est tout simplement une zone de texte présente dans la chaîne à construire et ayant une certaine largeur (ici 5), dite largeur du champ. La valeur de remplacement est placée dans le champ, alignée à droite et cette valeur est précédée d’espaces dans le champ.

Il se peut que la largeur du champ soit trop petite pour la valeur donnée et dans ce cas la valeur de remplacement sera complètement placée et la largeur sera ignorée :

1
2
s = "xxxxxxx%5dxxxxxxxxx" %9999999
print(s)
3
xxxxxxx9999999xxxxxxxxx

Les champs fonctionnent pour tout type de valeur (des entiers, des chaînes, etc):

1
2
s = "xxxxxxx%10sxxxxxxxxx" %"TOTO"
print(s)
3
xxxxxxx      TOTOxxxxxxxxx

Placement à gauche du champ

La valeur de remplacement dans un champ est, par défaut, alignée à droite. Une option permet d’aligner à gauche.

Si on place un signe - après le signe %, la valeur est placée à gauche du champ et est suivie d’espaces :

1
2
s = "xxxxxxx%-5dxxxxxxxxx" %42
print(s)
3
xxxxxxx42   xxxxxxxxx

Ce type d’option est appelé drapeau de conversion (conversion flag).

Zéros dominants

Une option de champ permet de faire précéder la valeur de remplacement d’un nombre par un certain nombre de zéros :

1
2
s = "James Bond %09d est anglais"%7
print(s)
3
James Bond 000000007 est anglais
  • Ligne 1 : le champ a pour largeur 9 et la valeur de remplacement (ici 7) sera précédée dans le champ par des zéros

Ce type d’option est appelé drapeau.

Formatage à champ variable

Ls chaînes formatées classiques proposent une syntaxe afin de créer des champs de longueur variable.

Au lieu d’écrire

1
2
s = "xxxxxxxxx%10dxxxxxx" %(42)
print(s)
3
xxxxxxxxx        42xxxxxx

on peut écrire

1
2
s = "xxxxxxxxx%*dxxxxxx" %(10, 42)
print(s)
3
xxxxxxxxx        42xxxxxx

Noter le symbole * avant le type de conversion (d). La valeur de la largeur est présente dans le tuple juste avant la valeur de remplacement.

L’avantage de la deuxième forme (avec *) est que le champ a alors une largeur (ici 10) que l’on peut rendre variable ce qui peut être intéressant puisqu’on peut donner un champ qui va dépendre de la donnée formatée (ici 42).

La largeur est placée dans un tuple juste avant la valeur à afficher. On a donc ici deux valeurs dans le tuple mais une seule valeur affichée.

Voici un exemple où la largeur de champ est vraiment variable :

1
2
3
4
5
6
gabarit = "..........%*s............."

for z in ["toto", "azertyuiop"]:
    s = gabarit %(3 * len(z), z)
    print(s)xxxxxx%*sxxxxxx" %(3 * len(z), z)
print(s)
7
8
..........        toto.............
..........                    azertyuiop.............

Il se peut que plusieurs champs soient variables :

1
2
s = "xxxx%*sxxx%*dxx%4dx" %(10,"TOTO", 5, 42, 81)
print(s)
3
xxxx      TOTOxxx   42xx  81x

Ici, on a affiché trois valeurs TOTO, 42 et 1000. Le deux premières sont à champ variable et la largeur du champ (ici 10 puis 5) apparait dans le tuple juste avant la valeur à afficher. Noter que la dernière a un champ non variable.

Pour chaque spécificateur de format qui commence par *, c’est d’abord la largeur qui est capturée dans le tuple puis la valeur de remplacement.

On peut utiliser un champ variable pour contrôler la partie décimale d’un flottant :

1
2
3
4
5
from math import pi

print("%.*f"%(8,pi))
print("%.*f"%(6, pi))
print()
6
7
3.14159265
3.141593

On peut d’ailleurs résoudre le problème sans recours à cette nouvelle syntaxe :

1
2
3
4
from math import pi

for precision in [8,6]:
    print("%%.%sf" % precision %pi)
5
6
3.14159265
3.141593

Cumuls d’options

On peut cumuler différentes options de formatage. Par exemple :

1
2
x = 3.1415926
print("pi = %10.2f (approximation)" % x)
3
pi =       3.14 (approximation)
  • Ligne 2 : cumul de champ et de précision à deux décimales
1
2
x = 3.1415926
print("pi = %-10.2f (approximation)" % x)
3
pi = 3.14       (approximation)
  • Ligne 2 : cumul de champ (10), d’alignement à gauche (le signe -) et de précision à deux décimales (.2)

Nombres

Espace automatique devant un nombre positif

On peut placer un espace devant une valeur numérique formatée en plaçant un espace après le signe % dans le format :

1
2
3
4
5
gabarit = "xxxxxxx% dxxxxxxxxx"
s = gabarit %42
t = gabarit %-42
print(s)
print(t)
6
7
xxxxxxx 42xxxxxxxxx
xxxxxxx-42xxxxxxxxx

L’intérêt est de faire des alignements verticaux avec des nombres négatifs car en cas de nombre négatif, aucune espace n’est placée.

Sans cette option, l’alignement n’est pas correct :

1
2
3
4
5
gabarit = "xxxxxxx%dxxxxxxxxx"
s = gabarit %42
t = gabarit %-42
print(s)
print(t)
6
7
xxxxxxx42xxxxxxxxx
xxxxxxx-42xxxxxxxxx

Ce type d’option est appelé drapeau.

Option de signe

Bien sûr, le formatage d’un nombre négatif impose un signe - devant le nombre formaté. Mais, par défaut, un nombre positif est formaté sans être précédé d’un signe, + :

1
2
3
4
5
gabarit = "x = %d"
s = gabarit %42
t = gabarit %-42
print(s)
print(t)
6
7
x = 42
x = -42

On peut forcer le placement d’un signe + devant une valeur numérique positive formatée en plaçant un signe + après le signe % dans le spécificateur :

1
2
3
4
5
gabarit = "x = %+d"
s = gabarit %42
t = gabarit %-42
print(s)
print(t)
6
7
x = +42
x = -42

L’effet est de permettre des alignement verticaux.

Ce type d’option est appelé drapeau.

Conversion d’un flottant avec %d

Le spécificateur %d appliqué à un flottant renvoie le nombre privé de sa partie décimale :

1
2
3
4
x = 421.99

print("%d" %x)
print("%d" %(-x))
5
6
421
-421

Formatage en bases 8 ou 16

Il est possible de formater une chaîne pour que les entiers apparaissent représentés en base 16 ou en base 8.

Pour la base 16, le spécificateur est %x ou %X :

1
2
3
4
s = "%d (base 10) = %x (base 16)" %(421, 421)
t = "%d (base 10) = %X (base 16)" %(421, 421)
print(s)
print(t)
5
6
421 (base 10) = 1a5 (base 16)
421 (base 10) = 1A5 (base 16)

On notera que les lettres hexadécimales sont formatées en minuscule avec le type x et en majuscule avec le type X.

Pour la base 8, le spécificateur est %o (la lettre o minuscule) :

1
2
s = "%d (base 10) = %o (base 8)" %(421, 421)
print(s)
3
421 (base 10) = 645 (base 8)

Par contre, il n’existe pas de caractère de conversion vers d’autres bases (comme la base 2 par exemple).

Formes alternées

La forme alternée est une syntaxe utilisant le caractère # dans un spécificateur de formatage et permettant, dans les cas les plus fréquents, un formatage en base 8 ou 16. Par exemple :

1
2
3
4
x = 42

print("%x" %x)
print("%#x" %x)
5
6
2a
0x2a
  • Ligne 4 : la forme alternée préfixe la représentation brute de la ligne 3

Les formes alternées les plus utilisées concernent l’ajout de préfixe dans le formatage avec le type o (octal) et les types x ou X (hexadécimal).

Le caractère # se place juste après le signe % sauf s’il y a une clé de dictionnaire pour le remplacement, auquel cas il se place juste après :

1
2
3
dico = {"my_x" : 42, "y" : 421}

print("%(my_x)#x" %dico)
4
0x2a

Ce type d’option est appelé drapeau.

Formatage d’un flottant en notation scientifique

Les spécificateurs de type %e ou %E formatent un nombre en notation scientifique :

1
2
3
4
5
x = 2030.45263
s = "%f (<- point décimal) = %e (<- ingenieur)" %(x,x)
t = "%f (<- point décimal) = %E (<- ingenieur)" %(x,x)
print(s)
print(t)
6
7
2030.452630 (<- point décimal) = 2.030453e+03 (<- ingenieur)
2030.452630 (<- point décimal) = 2.030453E+03 (<- ingenieur)

Divers

Méthode classique : statut, documentation

La méthode classique de formatage de chaîne est basée sur le style des chaînes formatées du langage C ou de Java utilisant l’opérateur % de substitution.

Selon la Documentation officielle :

  • le système classique comprendrait des « bizarreries » qui conduiraient à un certain nombre d’erreurs
  • la nouvelle syntaxe permet d’éviter ces erreurs mais qu’elle possède aussi ses inconvénients.

La documentation des versions 3.1 et 3.2 de Python affirmait :

The formatting operations described here are obsolete and may go away in future versions of Python. Use the new String Formatting in new code.

Toutefois cette mention a disparu des versions suivantes. A propos de cette disparition, on pourra lire la discussion Why was the warning about % formatting toned down in newer Python docs?.

La question de l’obsolescence du système classique reste posée et discutée.

A signaler (2021) que les chaînes binaires utilisent la méthode classique et qu’aucune méthode analogue à format ou les f-chaînes n’est envisagée, comme indiqué ici.

Liens vers la documentation :

Utilisation des chaînes formatées classiques

Le formatage pour créer une nouvelle chaîne

Une chaîne formatée est une chaîne comme les autres. Elle n’est pas exclusivement destinée à être affichée, bien que ce soit assez fréquent. Une chaîne formatée possède toutes les propriétés d’une chaîne, par exemple une longueur :

1
2
3
4
5
6
7
8
modele = "Son nom est %s et il a %s ans."

naissance = 2000
aujourdhui = 2030

a = modele %("Arthur", aujourdhui- naissance)
print(len(a))
print(a)
 9
10
34
Son nom est Arthur et il a 30 ans.

Donc, le formatage de chaîne est un outil de construction de nouvelles chaînes.

Usage typique

Les chaînes formatées servent souvent à remplacer des valeurs dans une chaîne-type puis à effectuer l’affichage (en général avec print). Par exemple :

1
2
3
4
5
6
modele = "%s = %s, %s = %s => %s + %s = %s"

x=42
y=18
print(modele %("x", x, "y", y, "x", "y", x+y))
print(modele %("toto", x, "titi", y, "toto", "titi", x+y))
7
8
x = 42, y = 18 => x + y = 60
toto = 42, titi = 18 => toto + titi = 60

Exemple avancé

Les chaînes formatées servent aussi à construire des chaînes complexes, représentant par exemple des alignements particuliers, pas forcément dans un but immédiat d’affichage, typiquement, pour réaliser une table de multiplication comme ceci :

 ---------------------------------------------------------------------
| 1 x  1 =   1| 2 x  1 =   2| 3 x  1 =   3| 4 x  1 =   4| 5 x  1 =   5|
| 1 x  2 =   2| 2 x  2 =   4| 3 x  2 =   6| 4 x  2 =   8| 5 x  2 =  10|
| 1 x  3 =   3| 2 x  3 =   6| 3 x  3 =   9| 4 x  3 =  12| 5 x  3 =  15|
| 1 x  4 =   4| 2 x  4 =   8| 3 x  4 =  12| 4 x  4 =  16| 5 x  4 =  20|
| 1 x  5 =   5| 2 x  5 =  10| 3 x  5 =  15| 4 x  5 =  20| 5 x  5 =  25|
| 1 x  6 =   6| 2 x  6 =  12| 3 x  6 =  18| 4 x  6 =  24| 5 x  6 =  30|
| 1 x  7 =   7| 2 x  7 =  14| 3 x  7 =  21| 4 x  7 =  28| 5 x  7 =  35|
| 1 x  8 =   8| 2 x  8 =  16| 3 x  8 =  24| 4 x  8 =  32| 5 x  8 =  40|
| 1 x  9 =   9| 2 x  9 =  18| 3 x  9 =  27| 4 x  9 =  36| 5 x  9 =  45|
| 1 x 10 =  10| 2 x 10 =  20| 3 x 10 =  30| 4 x 10 =  40| 5 x 10 =  50|
---------------------------------------------------------------------
| 6 x  1 =   6| 7 x  1 =   7| 8 x  1 =   8| 9 x  1 =   9|10 x  1 =  10|
| 6 x  2 =  12| 7 x  2 =  14| 8 x  2 =  16| 9 x  2 =  18|10 x  2 =  20|
| 6 x  3 =  18| 7 x  3 =  21| 8 x  3 =  24| 9 x  3 =  27|10 x  3 =  30|
| 6 x  4 =  24| 7 x  4 =  28| 8 x  4 =  32| 9 x  4 =  36|10 x  4 =  40|
| 6 x  5 =  30| 7 x  5 =  35| 8 x  5 =  40| 9 x  5 =  45|10 x  5 =  50|
| 6 x  6 =  36| 7 x  6 =  42| 8 x  6 =  48| 9 x  6 =  54|10 x  6 =  60|
| 6 x  7 =  42| 7 x  7 =  49| 8 x  7 =  56| 9 x  7 =  63|10 x  7 =  70|
| 6 x  8 =  48| 7 x  8 =  56| 8 x  8 =  64| 9 x  8 =  72|10 x  8 =  80|
| 6 x  9 =  54| 7 x  9 =  63| 8 x  9 =  72| 9 x  9 =  81|10 x  9 =  90|
| 6 x 10 =  60| 7 x 10 =  70| 8 x 10 =  80| 9 x 10 =  90|10 x 10 = 100|
 ---------------------------------------------------------------------

La table de multiplication est placée dans une grande chaîne au fur et à mesure. Ensuite, cette chaîne est affichée en une seule instruction.

Générer des formats

L’échappement du caractère % est utile, par exemple, pour générer des formats :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
formatFormat = "Son nom est %%%s et il a %%%s ans"
print(formatFormat)

print()

format_s_d = formatFormat %('s', 'd')
print(format_s_d)
t = ("Alfred", 100)
print(format_s_d %t)

print()

format_s_f = formatFormat %('s', 'f')
print(format_s_f)
t = ("Alfred", 100)
print(format_s_f %t)
17
18
19
20
21
22
23
Son nom est %%%s et il a %%%s ans

Son nom est %s et il a %d ans
Son nom est Alfred et il a 100 ans

Son nom est %s et il a %f ans
Son nom est Alfred et il a 100.000000 ans

Supposons que la chaîne formatFormat (ligne 1) soit utilisée comme un format dans une chaîne de formatage (cf. lignes 6 ou 13). Alors, la suite %%%s se décompose en :

  • %% qui va produire le caractère littéral % dans la chaîne formatée produite
  • %s qui est un spécificateur de formatage et qui va subir un remplacement (plus bas, par la lettre s ou encore la lettre f)

Ainsi, la chaîne formatFormat peut être utilisée comme format qui produit un format (par exemple, format_s_d) qui lui-même peut être utilisé pour obtenir une chaîne formatée.

Troncature de caractères

On peut formater une chaîne en en conservant qu’un préfixe de longeur donnée :

1
print("Le 19 %.3s 2038" %"janvier")
2
Le 19 jan 2038

Dans un spécificateur, un point suivi d’une valeur entière \(n\geq 0\) dans un formatage de type de conversion s (chaîne) entraîne la troncature à droite pour ne garder que les \(n\) premiers caractères.

Syntaxe complète

Un spécificateur de formatage peut contenir jusqu’à 5 options sans compter la lettre finale pour le type de conversion.

Voici un exemple utilisant un dictionnaire :

1
2
3
dico = {"nom":"Alfred", "age": 100}
s = "Son nom est %(nom)s et il a %(age)0 9.3f ans" %dico
print(s)
4
Son nom est Alfred et il a  0100.000 ans

Le spécificateur %(age)0 9.3f formate un flottant (à cause du type de conversion f) et admet la séquence d’options (age)0 9.3 et qui se décompose ainsi :

  • (age) qui est une clé du dictionnaire dico et qui est opérande de l’opérateur % (ligne 2)
  • 0 qui spécifie que le formatage du nombre pourra commencer par des zéros
  • l’espace suivant place un espace devant le nombre s’il est positif (comme on peut le voir dans la sortie).
  • 9 est une largeur de champ
  • .3 est un indicateur de précision à 3 décimales.

Voici un exemple utilisant un tuple :

1
2
3
t = ("Alfred", 11, 100)
s = "Son nom est %s et il a %#0 *x ans" %t
print(s)
4
Son nom est Alfred et il a  0x00000064 ans

Le spécificateur %#0 *.3x formate en hexadécimal un entier (à cause du type de conversion x) et admet la séquence d’options #0 *.3 et qui se décompose ainsi :

  • 0 qui spécifie que le formatage du nombre pourra commencer par des zéros
  • # forme alternée qui préfixe le formatage du nombre par 0x
  • l’espace suivant place un espace devant le nombre s’il est positif
  • * est une largeur de champ variable (11 ici) et qui est lue dans le tuple.

Ordre des spécificateurs

Les éléments d’un spécificateur doivent être donnés dans l’ordre suivant :

  • premier caractère : %
  • dernier caractère : une unique lettre (d, s, f, x, X, etc) désignant le type de conversion
  • si les données à remplacer proviennent d’un dictionnaire, la clé de remplacement doit apparaître juste après le signe %
  • ensuite, on place un ou plusieurs éventuels drapeaux #, 0, un espace ou les signes - ou + et qui ont un rôle de formatage en position préfixe du remplacement
  • ensuite, une éventuelle largeur de champ, éventuellement variable si on utilise le caractère *
  • enfin une précision formée d’un point et d’un entier définissant la troncature à droite, soit en décimales si le type de conversion est flottant (f) soit en caractères si le type de conversion est chaîne (s)

Formatage classique et concaténation

Le formatage de chaînes permet accessoirement de concaténer des chaînes

1
2
3
4
5
modele = "%s%s%s"
print(modele  %("Uni","Ver","Sel"))

modele = "%s-%s-%s"
print(modele  %("Uni","Ver","Sel"))
6
7
UniVerSel
Uni-Ver-Sel