web-dev-qa-db-fra.com

Un moyen d'imprimer correctement les dictionnaires ordonnés?

J'aime le module pprint en Python. Je l'utilise beaucoup pour tester et déboguer. J'utilise fréquemment l'option width pour m'assurer que la sortie s'adapte bien à la fenêtre de mon terminal.

Cela a bien fonctionné jusqu'à ce qu'ils ajoutent le nouveau dictionnaire de type ordonné dans Python 2.7 (une autre fonctionnalité intéressante que j'aime beaucoup). Si j'essaie d'imprimer un dictionnaire en ordre, cela ne se voit pas bien. Au lieu d’avoir chaque paire clé-valeur sur sa propre ligne, le tout apparaît sur une longue ligne, qui est renvoyée à plusieurs reprises et qui est difficile à lire.

Est-ce que quelqu'un ici a un moyen de le faire bien imprimer, comme les vieux dictionnaires non ordonnés? Je pourrais probablement trouver quelque chose, éventuellement en utilisant la méthode PrettyPrinter.format, si je passe suffisamment de temps, mais je me demande si quelqu'un ici connaît déjà une solution.

UPDATE: J'ai déposé un rapport de bogue pour cela. Vous pouvez le voir sur http://bugs.python.org/issue10592 .

84
Elias Zamaria

En guise de solution de contournement temporaire, vous pouvez essayer le dumping au format JSON . Vous perdez certaines informations de type, mais elles ont l'air bien et conservent la commande.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^Nice
116
webwurst

Ce qui suit fonctionnera si l'ordre de votre OrderedDict est un tri alpha, car pprint triera un dict avant impression.

pprint(dict(o.items()))
14
kzh

Voici une autre réponse qui fonctionne en remplaçant et en utilisant la fonction stock pprint() en interne. Contrairement à mon précédent il sera gérer OrderedDict dans un autre conteneur tel qu'un list et devrait également pouvoir gérer arguments de mot clé facultatifs donnés - toutefois, il n’exerce pas le même degré de contrôle sur la sortie que l’autre.

Il fonctionne en redirigeant la sortie de la fonction stock dans un tampon temporaire, puis Word le retourne avant de l'envoyer au flux de sortie. Bien que la sortie finale produite ne soit pas exceptionnellement jolie, elle est décente et peut être "assez bonne" pour être utilisée comme solution de contournement.

Mise à jour 2.0

Simplifié en utilisant le module standard textwrap de la bibliothèque standard, et modifié pour fonctionner à la fois dans Python 2 & 3.

from collections import OrderedDict
try:
    from cStringIO import StringIO
except ImportError:  # Python 3
    from io import StringIO
from pprint import pprint as pp_pprint
import sys
import textwrap

def pprint(object, **kwrds):
    try:
        width = kwrds['width']
    except KeyError: # unlimited, use stock function
        pp_pprint(object, **kwrds)
        return
    buffer = StringIO()
    stream = kwrds.get('stream', sys.stdout)
    kwrds.update({'stream': buffer})
    pp_pprint(object, **kwrds)
    words = buffer.getvalue().split()
    buffer.close()

    # Word wrap output onto multiple lines <= width characters
    try:
        print >> stream, textwrap.fill(' '.join(words), width=width)
    except TypeError:  # Python 3
        print(textwrap.fill(' '.join(words), width=width), file=stream)

d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
lod = [OrderedDict((('john',1), ('paul',2), ('mary',3))),
       OrderedDict((('moe',1), ('curly',2), ('larry',3))),
       OrderedDict((('weapons',1), ('mass',2), ('destruction',3)))]

Exemple de sortie:

pprint(d, width=40)

"{'john': 1, 'mary': 3, 'paul': 2}

pprint(od, width=40)

"OrderedDict([('john', 1), ('paul', 2),
('mary', 3)])

pprint(lod, width=40)

"[OrderedDict([('john', 1), ('paul', 2),
('mary', 3)]), OrderedDict([('moe', 1),
('curly', 2), ('larry', 3)]),
OrderedDict([('weapons', 1), ('mass',
2), ('destruction', 3)])]

8
martineau

Pour imprimer un dict ordonné, par exemple.

from collections import OrderedDict

d=OrderedDict([
    ('a', OrderedDict([
        ('a1',1),
        ('a2','sss')
    ])),
    ('b', OrderedDict([
        ('b1', OrderedDict([
            ('bb1',1),
            ('bb2',4.5)])),
        ('b2',4.5)
    ])),
])

Je fais

def dict_or_OrdDict_to_formatted_str(OD, mode='dict', s="", indent=' '*4, level=0):
    def is_number(s):
        try:
            float(s)
            return True
        except ValueError:
            return False
    def fstr(s):
        return s if is_number(s) else '"%s"'%s
    if mode != 'dict':
        kv_tpl = '("%s", %s)'
        ST = 'OrderedDict([\n'; END = '])'
    else:
        kv_tpl = '"%s": %s'
        ST = '{\n'; END = '}'
    for i,k in enumerate(OD.keys()):
        if type(OD[k]) in [dict, OrderedDict]:
            level += 1
            s += (level-1)*indent+kv_tpl%(k,ST+dict_or_OrdDict_to_formatted_str(OD[k], mode=mode, indent=indent, level=level)+(level-1)*indent+END)
            level -= 1
        else:
            s += level*indent+kv_tpl%(k,fstr(OD[k]))
        if i!=len(OD)-1:
            s += ","
        s += "\n"
    return s

print dict_or_OrdDict_to_formatted_str(d)

Quels rendements

"a": {
    "a1": 1,
    "a2": "sss"
},
"b": {
    "b1": {
        "bb1": 1,
        "bb2": 4.5
    },
    "b2": 4.5
}

ou

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

qui donne

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))
7
Ilya Prokin

Voici un moyen de pirater l’implémentation de pprint.pprint pour trier les clés avant l’impression. Pour préserver l’ordre, il suffit donc de trier les clés à la manière souhaitée.

Notez que cela a un impact sur la fonction items() . Il est donc utile de conserver et de restaurer les fonctions remplacées après l’impression p.

from collections import OrderedDict
import pprint

class ItemKey(object):
  def __init__(self, name, position):
    self.name = name
    self.position = position
  def __cmp__(self, b):
    assert isinstance(b, ItemKey)
    return cmp(self.position, b.position)
  def __repr__(self):
    return repr(self.name)

OrderedDict.items = lambda self: [
    (ItemKey(name, i), value)
    for i, (name, value) in enumerate(self.iteritems())]
OrderedDict.__repr__ = dict.__repr__

a = OrderedDict()
a[4] = '4'
a[1] = '1'
a[2] = '2'
print pprint.pformat(a) # {4: '4', 1: '1', 2: '2'}
5
rumpel
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

Voilà

for item in li:
    pprint_od(item)

ou

(pprint_od(item) for item in li)
2
Jakob Bowyer

C'est assez grossier, mais j'avais juste besoin d'un moyen de visualiser une structure de données constituée de mappages et d'itables arbitraires et c'est ce que j'ai proposé avant d'abandonner. C'est récursif, donc il va très bien tomber dans les structures imbriquées et les listes. J'ai utilisé les classes de base abstraites Mapping et Iterable des collections pour gérer à peu près tout.

Je visais presque une sortie similaire à yaml avec un code python concis, mais je n’ai pas réussi à le faire.

def format_structure(d, level=0):
    x = ""
    if isinstance(d, Mapping):
        lenk = max(map(lambda x: len(str(x)), d.keys()))
        for k, v in d.items():
            key_text = "\n" + " "*level + " "*(lenk - len(str(k))) + str(k)
            x += key_text + ": " + format_structure(v, level=level+lenk)
    Elif isinstance(d, Iterable) and not isinstance(d, basestring):
        for e in d:
            x += "\n" + " "*level + "- " + format_structure(e, level=level+4)
    else:
        x = str(d)
    return x

et quelques données de test utilisant OrderedDict et des listes de OrderedDicts ... (sheesh Python a besoin de littéraux de OrderedDict tellement terriblement mauvais ...)

d = OrderedDict([("main",
                  OrderedDict([("window",
                                OrderedDict([("size", [500, 500]),
                                             ("position", [100, 900])])),
                               ("splash_enabled", True),
                               ("theme", "Dark")])),
                 ("updates",
                  OrderedDict([("automatic", True),
                               ("servers",
                                [OrderedDict([("url", "http://server1.com"),
                                              ("name", "Stable")]),
                                 OrderedDict([("url", "http://server2.com"),
                                              ("name", "Beta")]),
                                 OrderedDict([("url", "http://server3.com"),
                                              ("name", "Dev")])]),
                               ("Prompt_restart", True)])),
                 ("logging",
                  OrderedDict([("enabled", True),
                               ("rotate", True)]))])

print format_structure(d)

donne le résultat suivant:

   main: 
               window: 
                         size: 
                             - 500
                             - 500
                     position: 
                             - 100
                             - 900
       splash_enabled: True
                theme: Dark
updates: 
            automatic: True
              servers: 
                     - 
                          url: http://server1.com
                         name: Stable
                     - 
                          url: http://server2.com
                         name: Beta
                     - 
                          url: http://server3.com
                         name: Dev
       Prompt_restart: True
logging: 
       enabled: True
        rotate: True

J'avais quelques idées sur l'utilisation de str.format () pour un meilleur alignement, mais je n'avais pas envie de creuser. Vous devez spécifier dynamiquement les largeurs de champ en fonction du type d'alignement souhaité, ce qui peut s'avérer délicat ou fastidieux.

Quoi qu'il en soit, cela me montre mes données de manière hiérarchique lisible, alors ça marche pour moi!

2
flutefreak7

La méthode pprint() appelle simplement la méthode __repr__() dans laquelle elle se trouve, et OrderedDict ne semble pas faire grand chose dans sa méthode (ou n'en a pas un ou quelque chose). 

Voici une solution peu coûteuse qui devrait fonctionner SI VOUS NE FAITES PAS DE L'ORDRE VISIBLE DANS LA SORTIE DU PPRINT , ce qui peut être un problème si:

class PrintableOrderedDict(OrderedDict):
    def __repr__(self):
        return dict.__repr__(self)

Je suis en fait surpris que l'ordre ne soit pas préservé ... ah bien.

1
Bill M.

Si les éléments du dictionnaire sont tous d'un seul type, vous pouvez utiliser l'étonnante bibliothèque de traitement de données pandas:

>>> import pandas as pd
>>> x = {'foo':1, 'bar':2}
>>> pd.Series(x)
bar    2
foo    1
dtype: int64

ou

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object
0
LondonRob

Vous pouvez également utiliser cette simplification de la réponse kzh :

pprint(data.items(), indent=4)

Il conserve l’ordre et produira presque le même résultat que webwurst answer (print via json dump).

0
Albert Alomar

Vous pouvez redéfinir pprint() et intercepter les appels de OrderedDict. Voici une illustration simple. Tel qu'écrit, le code de substitution OrderedDict ignore les mots clés stream, indent, width ou depth facultatifs qui ont peut-être été transmis, mais qui pourraient être améliorés pour les implémenter. Malheureusement, cette technique ne les gère pas dans un autre conteneur, tel que list de OrderDict 's 

from collections import OrderedDict
from pprint import pprint as pp_pprint

def pprint(obj, *args, **kwrds):
    if not isinstance(obj, OrderedDict):
        # use stock function
        return pp_pprint(obj, *args, **kwrds)
    else:
        # very simple sample custom implementation...
        print "{"
        for key in obj:
            print "    %r:%r" % (key, obj[key])
        print "}"

l = [10, 2, 4]
d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
pprint(l, width=4)
# [10,
#  2,
#  4]
pprint(d)
# {'john': 1, 'mary': 3, 'paul': 2}

pprint(od)
# {
#     'john':1
#     'paul':2
#     'mary':3
# }
0
martineau

J'ai testé ce hack basé sur le patch de singe sur python3.5 et cela fonctionne:

pprint.PrettyPrinter._dispatch[pprint._collections.OrderedDict.__repr__] = pprint.PrettyPrinter._pprint_dict


def unsorted_pprint(data):
    def fake_sort(*args, **kwargs):
        return args[0]
    orig_sorted = __builtins__.sorted
    try:
        __builtins__.sorted = fake_sort
        pprint.pprint(data)
    finally:
        __builtins__.sorted = orig_sorted

Vous faites en sorte que pprint utilise le résumé habituel basé sur dict et que vous désactiviez également le tri pendant la durée de l'appel afin qu'aucune clé ne soit réellement triée pour l'impression.

0
Karl Rosaen

Voici mon approche de la jolie impression d'un OrderedDict

from collections import OrderedDict
import json
d = OrderedDict()
d['duck'] = 'alive'
d['parrot'] = 'dead'
d['penguin'] = 'exploded'
d['Falcon'] = 'discharged'
print d
print json.dumps(d,indent=4)

OutPut:

OrderedDict([('duck', 'alive'), ('parrot', 'dead'), ('penguin', 'exploded'), ('Falcon', 'discharged')])

{
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded",
    "Falcon": "discharged"
}

Si vous voulez imprimer un dictionnaire avec les clés dans l’ordre trié

print json.dumps(indent=4,sort_keys=True)
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
0
CHINTAN VADGAMA