Impression d'une chaine de caractère en utilisant un dictionnaire pour le format

Fermé
getudir78 Messages postés 29 Date d'inscription vendredi 3 janvier 2014 Statut Membre Dernière intervention 2 septembre 2020 - Modifié le 9 déc. 2019 à 00:11
 trifou - 11 déc. 2019 à 18:51
Bonjour,

Je viens de mettre en ligne mon fichier maladroitement et je n'y ai joint aucun commentaire.

Dans ce cas d'école, j'imprime une chaîne de caractères de 3 façons différentes :
1 - les paramètres du format sont définis manuellement; ça marche.
2 - les paramètres du format sont extraits manuellement d'une liste de chaînes de caractères; ça marche.
3 - pour rendre le format automatique pour un nombre variable de chaînes de caractères, j'ai mis les paramètres du format dans un dictionnaire; le dictionnaire me paraît correct.
Par contre, quand je veux imprimer la même chaîne de caractères qu'en 1 - et 2 -, en utilisant .format(**dict_s), j'obtiens le message d'erreur suivant :
IndexError: Replacement index 1 out of range for positional args tuple

Je ne comprends pourquoi ça ne marche pas.

Si vous pouvez éclairer le débutant que je suis, je vous en remercie par avance.

Cordialement

--------------------------------------------------------------------------------------------------------------------------------------------------------
Voici le programme en question :

""" But : faire des tests de print avec .format """
print("")
Texte_pour_impression = "Né à {1}; Etudes à  {2}, premier poste d'ingénieur  à {0}, c'est imprimé !"
print("1 - arguments du format directs ")
print(Texte_pour_impression.format("Capitale", "Trifouilly-les-oies", "Ville-grande"))

print("")
liste_ville = ["Capitale", "Trifouilly-les-oies", "Ville-grande"]
print("2 - arguments du format dans une chaine de caractères ")
print(Texte_pour_impression.format(liste_ville[0] ,liste_ville[1], liste_ville[2]))

print("")
print("arguments dans in dictionnaire")

#création d'un dictionnaire  pour les arguments du format d'impression
dict_s = {}
i = 0
for s in liste_ville :
 #dict_s[str(i)] = liste_ville[i]
 dict_s[str(i)] = liste_ville[i]
 i += 1
 
print("dictionnaire ainsi créé = ", dict_s, " type =",type(dict_s))
print("")  
print("3 - arguments du format dans le dictionnaire ci-dessus ")
# Le print suivant donne une erreur que je ne comprends pas
print(Texte_pour_impression.format(**dict_s))


Configuration: Windows / Firefox 71.0
A voir également:

1 réponse

Bonjour,

Cela vient du fait que les keys du dict sont numériques, format n'accepte pas cela pour un dict.

Donc il faudrait faire pour au départ une simple liste

s = '{1} {0} {2} {4} {3}'
digits = ['zéro', 'un', 'deux', 'trois', 'quatre']
print('Format list', s)
print(s.format(*digits))


La même chose pour le dict

dic = {i:v for i, v in enumerate(digits)}
print('Format dict', s)
print(s.format(*dic.values()))


On pourrait croire que ça fonctionne comme on veut, sauf que non, car les dict sont par nature non ordonné.

dic = {i:v for i, v in enumerate(digits[1:], 1)}
dic[0] = digits[0]
print(s.format(*dic.values()))


Donc il faut trier pour obtenir la même chose que la liste de départ
print(s.format(*(dic[i] for i in sorted(dic))))


Conclusion :
Il est préférable de ne pas utiliser de clef numérique pour les dict surtout si on a besoin de l'afficher avec format ^^
0
getudir78 Messages postés 29 Date d'inscription vendredi 3 janvier 2014 Statut Membre Dernière intervention 2 septembre 2020 33
9 déc. 2019 à 15:14
Bonjour,

Merci pour ton analyse détaillée qui m’a permis d’y voir clair et de résoudre mon problème d’impression. Pour faire le point, si j’ai bien compris :
- 1 - quand on crée un dictionnaire, l’ordre des clés utilisées pour sa construction se retrouve dans le dictionnaire qui en est le reflet. A sa création, le dictionnaire est alors ordonné et le restera si on ne le modifie pas
- 2 - quand on rajoute un nouvel item au dictionnaire, l’ordre n’est plus certain et il faut alors ordonner les clés par une fonction anonyme, pour retrouver l’ordre souhaité.
- 3 - *dictionnaire donne à toutes les clés du dictionnaire et *dictionnaire.values() parcourt les valeurs correspondantes.
-
Peux-tu me confirmer qu’il en est bien ainsi ?


Il reste un point mystérieux concernant les ** appliquées à des dictionnaires: Les clés du dictionnaire que j’ai créé, ne sont pas des entiers mais des strings d’entiers ; par exemple la clé d’indice 0 n’est pas 0 mais ‘0’, qui est donc un caractère.
Néanmoins, **dictionnaire crée une erreur d’indice ; :
IndexError: Replacement index 1 out of range for positional args tuple

Puisque les clés ne sont pas des entiers, d’où provient cette erreur ?

L’essentiel, c’est que maintenant, je dispose d une solution claire et viable pour imprimer du texte avec un format utilisant un dictionnaire.

Sois-en encore remercié.

Tous mes remerciements également à « Comment ça marche » pour ce remarquable forum.

Cordialement
0
trifou > getudir78 Messages postés 29 Date d'inscription vendredi 3 janvier 2014 Statut Membre Dernière intervention 2 septembre 2020
9 déc. 2019 à 16:53
Depuis une récente version de python, un dict reste ordonné selon l'ordre des insertions effectuées (comme un append d'une liste), ce n'était pas le cas avant.
Si on souhaite toujours avoir un dict ordonné par clés, il faut se tourner vers la classe OrderedDict du module collections.

Oui c'est ça pour *dictionnaire, c'est un raccourci de *dictionnaire.keys()

Puisque les clés ne sont pas des entiers, d’où provient cette erreur ?

En fait, ce n'est pas spécifique aux entiers, mais à toute clé de dictionnaire non conforme, elle doit avoir les mêmes règles d'écriture que la déclaration d'une variable typique pour pouvoir être utilisée avec format.

https://docs.python.org/3/library/string.html#format-string-syntax

... it is not possible to specify arbitrary dictionary keys (e.g., the strings '10' or ':-]') within a format string.

C'est pareil aux règles des arguments nommés des fonctions finalement.
0
getudir78 Messages postés 29 Date d'inscription vendredi 3 janvier 2014 Statut Membre Dernière intervention 2 septembre 2020 33
10 déc. 2019 à 21:15
Bonsoir,

Merci pour toutes ces infos précises.
Je ne vais pas trop dans la doc de Python parce qu'elle n'est pas toujours simple à comprendre Je devrais néanmoins m'y plonger dans l'espoir que ça finira par s'éclaircir.

Comme je travaille avec Python 3.8, je bénéficie des propriétés d'ordre des dictionnaires qui sont nouvelles, ce dont je n'avais pas conscience; mais c'est dangereux car si mon modeste code s'exécute sur une version antérieure, ça ne fonctionnera plus.

La doc est sans appel, ** ne peut pas s'appliquer au dictionnaire que j'ai créé, à cause des clés du type {'entier'}

En ce qui concerne les impressions de chaînes de caractères utilisant .format(), j'ai finalement trouvé un raccourci très simple :
"chaîne de caractère".format(*liste ).
Pour les exemples que tu proposes, ça serait
print(s.format(*digits))
et pour les impressions de mon programme test, ça donnerait
print(Texte_pour_impression.format(*liste_ville)).
C'est plus simple que de construire un dictionnaire, mais je ne sais pas ce que ça donne avec les versions antérieures de Python.

Que penses-tu de ce raccourci pour créer les bons arguments à .format() ?

Cordialement,
0
trifou > getudir78 Messages postés 29 Date d'inscription vendredi 3 janvier 2014 Statut Membre Dernière intervention 2 septembre 2020
11 déc. 2019 à 18:51
Bonsoir,

Tout dépend du contexte du code, on n'a généralement pas besoin de formater une chaîne avec format pour qu'elle soit compatible à la fois avec différents types de structures en paramètres, et généralement on utilise autre chose comme clés d'un dictionnaire que de simples numériques. Même si on peut le faire, on aura sans doute jamais besoin de passer un dictionnaire de ce type directement à format.
0