Outils pour utilisateurs

Outils du site


arrond

Un affichage paramétrable des nombres réels et des chaines dans des listes, tuples, dictionnaires et nombres complexes

Problématique

En python, on sait afficher un nombre réel (à virgule flottante) avec le nombre de décimale qu'il faut:

print round(0.57338244358883583,5)
0.57338
 
print "%.5f" % 0.57338244358883583
0.57338

Mais quand on a une liste, ou même un arbre (une liste de liste de liste…), ça ne marche plus!

Par exemple:

[12.321456987,5.789654123,123.789654123]

Comment je fais pour l'afficher avec 4 chiffres seulement après la virgule pour tous les nombres réels qui se trouvent dedans? On peut avoir recours à des opérations sur les listes, mais ce n'est pas simple, cela ne donne pas toujours des résultats corrects, et cela ne marche plus pour les arbres (liste de liste de listes…):

print map(lambda x: round(x,4), [12.321456987,5.789654123,123.789654123])
[12.3215, 5.7896999999999998, 123.7897]

On a souvent aussi à “échapper” aux affichages bizarres des nombres réels dans une liste, du genre:

print 0.1
0.1
print [0.1]
[0.10000000000000001]

Le problème se pose aussi pour les tuples, les dictionnaires, et les nombres complexes, ainsi, bien sûr, pour les données imbriquées les unes dans les autres (liste de tuples de listes…).

Enfin, lorsque des caractères accentués sont dans une liste, les instructions “print” donnent des résultats curieux, parce qu'ils dépendent de l'encodage de la console dans laquelle on affiche.

print "éàç"
"éàç"
print ["éàç"]
['\xe9\xe0\xe7']

Pour avoir un code portable, il faut connaitre l'encodage des chaines qu'on manipule, et les convertir automatiquement avec l'encodage voulu (de la console par exemple).

Voilà une solution.

Solution proposée

Le code proposé est récursif.

Il traite les structures de base suivantes de Python:

  • nombre entier et entier long
  • nombre réel,
  • nombre complexe,
  • chaînes de caractères (encodage pour le périphérique de sortie)
  • liste et liste de liste (quelque soit sa profondeur),
  • tuple et tuple de tuple (quelque soit sa profondeur),
  • dictionnaire (traitement des valeurs, mais pas des clés!),
  • n'importe quel mélange des catégories de données ci-dessus.

Le résultat est une chaîne de caractères qui sera utilisée telle quelle.

On a le choix entre les 2 types d'affichage suivants pour les nombres réels (à virgule flottante):

  • L'affichage avec n<0 (défaut n=-15) correspond à la lettre “g” du format “%”, c'est à dire que n représente le nombre de chiffres significatifs (pas forcément après la virgule).
  • L'affichage avec n>=0 correspond à la lettre “f” du format “%”, c'est à dire que n représente le nombre de chiffres après la virgule.

Le code est suffisamment simple pour être adapté à des cas différents, non traités ici, comme par exemple pour affichage des nombres avec exposants.

La fonction est conçue pour l'affichage dans une console, mais peut aussi servir dans n'importe quelle autre utilisation, y compris pour l'enregistrement sur disque.

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import sys
 
def formate(x, nbc=-15, decod="utf-8", encod=None):
    """transforme en chaine un objet imprimable avec le bon formatage des flottants et le bon encodage des chaines
  - nbc: nombre de chiffres pour les flottants:
    - Si nbc<0: abs(nbc) chiffres significatifs.
    - Si nbc>=0: nbc chiffres après la virgule.
  - decod: encodage des chaines non-unicode (pour les chaines du code source: mettre l'encodage du code source)
  - encod: encodage voulu pour la chaine de sortie (pour l'affichage: trouve encodage de la console)
"""
 
    if encod==None:
        encod = sys.stdout.encoding
        if encod==None:
            encod = 'utf-8'  # cas particulier de la console d'eclipse
 
    if isinstance(x,int) or isinstance(x,long):
        return "%d" % (x)
 
    elif isinstance(x,float):
        f = "%d" % (abs(nbc))
        if nbc<0:
            f = "%." + f + "g"
        else:
            f = "%." + f + "f"
        return f % (x)
 
    elif isinstance(x,complex):
        L = "("
        y = x.real
        if isinstance(y,float) and float(int(y))==y:
            y=int(y)
        L += formate(y,nbc,encod,decod)
        y = x.imag
        if isinstance(y,float) and float(int(y))==y:
            y=int(y)
        if y>=0:
            L += "+"
        L += formate(y,nbc,encod,decod) + "j)"
        return L
 
    elif isinstance(x,str):
        return '"%s"' % (x.decode(decod).encode(encod,'replace'))
 
    elif isinstance(x,unicode):
        return '"%s"' % (x.encode(encod,'replace'))
 
    elif isinstance(x,list):
        L = "["
        if x!=[]:
            for elem in x:
                L += "%s, " % formate(elem,nbc,decod,encod)
            L=L[:-2]
        L += "]"
        return L
 
    elif isinstance(x,tuple):
        L = "("
        if x!=():
            for elem in x:
                L += "%s, " % formate(elem,nbc,decod,encod)
            L = L[:-2]
        L += ")"
        return L
 
    elif isinstance(x,dict):
        L = "{"
        if x!={}:
            for k, v in x.iteritems():
                L += '%s:%s, ' % (formate(k,nbc,decod,encod), formate(v,nbc,decod,encod))
            L = L[:-2]
        L += "}"
        return L
 
    else:
        return "%s" % (x)

Quelques exemples d'utilisation (les résultats attendus sont en commentaires):

x = [0.92498894346812754, [6127.6807831692537, 7.6312137788054568, [95.047644748356008, 0.21995139819419918, 0.31819577813609723], 0.035713503484146036], 0.89938839028430906]
print formate(x,-5)
# [0.92499, [6127.7, 7.6312, [95.048, 0.21995, 0.3182], 0.035714], 0.89939]
print formate(x,5)
# [0.92499, [6127.68078, 7.63121, [95.04764, 0.21995, 0.31820], 0.03571], 0.89939]
 
x=[0.1]
print formate(x)
# [0.1]
 
x=complex(0.271165409484, 0.278306542098)
print formate(x,5)
# (0.27117+0.27831j)
 
x=complex(0.271165409484, -0.278306542098)
print formate(x,5)
# (0.27117-0.27831j)
 
x=complex(0.271165409484, 5)
print formate(x,5)
# (0.27117+5j)
 
x={"a":5, "toto":0.626229260038258, 4:10, 1.32456:0.587782755128, "c":"abcéèàçïô"}
print formate(x,5)
# {"a":5, 4:10, "c":"abcéèàçïô", "toto":0.62623, 1.32456:0.58778}
 
x = [12.321456987,5.789654123,123,"abcéèàçïô",(78.197586424365,56.576284659321,u"abcéèàçïô")]
print formate(x,4)
# [12.3215, 5.7897, 123, "abcéèàçïô", (78.1976, 56.5763, "abcéèàçïô")]
 
x = "abcéèàçïô"
print x
# abcéèàçïô
print formate(x)
# "abcéèàçïô"
 
x = u"abcéèàçïô"
print x
# abcéèàçïô
print formate(x)
# "abcéèàçïô"
 
x = ["abcéèàçïô", u"abcéèàçïô"]
print x
# ['abc\xc3\xa9\xc3\xa8\xc3\xa0\xc3\xa7\xc3\xaf\xc3\xb4', u'abc\xe9\xe8\xe0\xe7\xef\xf4']
print formate(x)
# [abcéèàçïô, abcéèàçïô]

Amusez-vous bien (et dieu sait qu'il n'est pas facile de s'amuser avec les encodages :-D )

arrond.txt · Dernière modification: 2009/09/04 13:21 par tyrtamos

Outils de la page