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

97

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'intègre parfaitement dans la fenêtre de mon terminal.

Cela a bien fonctionné jusqu'à ce qu'ils ajoutent le nouveau type de dictionnaire ordonné dans Python 2.7 (une autre fonctionnalité intéressante que j'aime vraiment). Si j'essaie d'imprimer un dictionnaire ordonné, il ne s'affiche pas bien. Au lieu d'avoir chaque paire clé-valeur sur sa propre ligne, le tout apparaît sur une longue ligne, qui s'enroule plusieurs fois et est difficile à lire.

Quelqu'un ici a-t-il un moyen de le faire imprimer correctement, comme les anciens dictionnaires non ordonnés? Je pourrais probablement trouver quelque chose, peut-être 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.

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

Elias Zamaria
la source
2
Suggérer d'ajouter un commentaire sur le dictionnaire ordonné à bugs.python.org/issue7434
Ned Deily

Réponses:

133

Pour contourner le problème, vous pouvez essayer de vider au format JSON. Vous perdez des informations de type, mais elles sont belles et conservent l'ordre.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
Webwurst
la source
7
@scottmrogowski Pourquoi pas simplement pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))fonctionne bien si vous ne vous souciez pas de l'ordre des clés. Personnellement, je souhaite que le __repr__for OrderedDictproduise une sortie comme celle-ci mais conserve l'ordre des touches.
ws_e_c421
9
@Alfe si le dict a imbriqué OrderedDicts, ils ne seront pas affichés
correctement
1
Échoue
2
@Alfe: Parce qu'alors la sortie n'est pas ordonnée. La raison pour laquelle OrderedDict est utilisé au lieu de dict en premier lieu, c'est que l'ordre est important.
Teekin
15

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

pprint(dict(o.items()))
kzh
la source
2
Puisque les OrderedDicts sont classés par ordre d'insertion, cela s'applique probablement à un petit pourcentage d'utilisations. Quoi qu'il en soit, la conversion de l'OD en un dictdevrait éviter que tout soit placé sur une seule ligne.
martineau
8

Voici une autre réponse qui fonctionne en remplaçant et en utilisant la pprint()fonction stock en interne. Contrairement à mon antérieur il se gérer OrderedDictest dans un autre récipient tel qu'un listet devrait également être capable de gérer tous les arguments mot - clé donné en option - mais il n'a pas le même degré de contrôle sur la sortie que l'autre donnait.

Il fonctionne en redirigeant la sortie de la fonction stock dans un tampon temporaire, puis le mot encapsule cela 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 textwrapmodule de bibliothèque standard et modifié pour fonctionner à la fois en Python 2 et 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)])]

Martineau
la source
J'ai essayé ça et ça marche. Comme vous l'avez dit, ce n'est pas la plus jolie, mais c'est la meilleure solution que j'ai vue jusqu'à présent.
Elias Zamaria
7

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)
]))
Ilya Prokin
la source
5

Voici un moyen de pirater la mise en œuvre de pprint. pprinttrie les clés avant l'impression, donc pour préserver l'ordre, il suffit de faire trier les clés comme nous le souhaitons.

Notez que cela a un impact sur la items()fonction. Vous voudrez peut-être conserver et restaurer les fonctions remplacées après avoir effectué le pprint.

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'}
rumpel
la source
2
Bien, mais mieux vaut sous-taper que remplacer les fonctions.
xmedeko
3

Voici mon approche pour imprimer 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 joli dictionnaire avec des clés dans un ordre trié

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
la source
@AlxVallejo Vous utilisez peut-être python3. S'il vous plaît vérifier
CHINTAN VADGAMA
2

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

Je visais une sortie presque yaml avec un code Python concis, mais je n'ai pas tout à fait réussi.

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 en utilisant OrderedDict et des listes de OrderedDicts ... (sheesh Python a tellement besoin de littéraux OrderedDict ...)

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 la sortie suivante:

   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'ai eu quelques réflexions sur l'utilisation de str.format () pour un meilleur alignement, mais je n'avais pas envie de creuser dedans. Vous auriez besoin de spécifier dynamiquement les largeurs de champ en fonction du type d'alignement souhaité, ce qui deviendrait soit délicat, soit encombrant.

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

flutefreak7
la source
2
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

Et voilà ^^

for item in li:
    pprint_od(item)

ou

(pprint_od(item) for item in li)
Jakob Bowyer
la source
Je recherche un moyen d'avoir une fonction capable d'imprimer des OrderedDicts ainsi que d'autres types. Je ne vois pas comment j'utiliserais votre fonction pour imprimer, disons, une liste de OrderedDicts.
Elias Zamaria
-1 La pprint_od()fonction ne fonctionne pas - l' for key, item in odinstruction donne un ValueError: too many values to unpack et la seule sortie indentée est la finale " }" et le key, itemdans l' printinstruction doit être entre parenthèses. Et voilà ^^
martineau
2

J'ai testé ce hack basé sur un patch de singe impie 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 pprintutilisez le récapitulatif habituel basé sur dict et désactivez également le tri pendant la durée de l'appel afin qu'aucune clé ne soit réellement triée pour l'impression.

Karl Rosaen
la source
vous pouvez également simplement copier le en pretty_print.pytant que module local et le pirater (en supprimant l' sortedappel ou ce que vous voulez).
Karl Rosaen
2

À partir de Python 3.8: pprint.PrettyPrinterexpose le sort_dictsparamètre de mot - clé.

True par défaut, le définir sur False laissera le dictionnaire non trié.

>>> from pprint import PrettyPrinter

>>> x = {'John': 1,
>>>      'Mary': 2,
>>>      'Paul': 3,
>>>      'Lisa': 4,
>>>      }

>>> PrettyPrinter(sort_dicts=False).pprint(x)

Sortira:

{'John': 1, 
 'Mary': 2, 
 'Paul': 3,
 'Lisa': 4}

Référence: https://docs.python.org/3/library/pprint.html

mxdbld
la source
1

La pprint()méthode appelle simplement la __repr__()méthode des choses qu'elle OrderedDictcontient , et ne semble pas faire grand-chose dans sa méthode (ou n'en a pas ou quelque chose).

Voici une solution bon marché qui devrait fonctionner SI VOUS NE VOUS SOUHAITEZ PAS QUE LA COMMANDE EST VISIBLE DANS LA SORTIE PPRINT , ce qui peut être un gros si:

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

Je suis en fait surpris que l'ordre ne soit pas conservé ... eh bien.

Bill M.
la source
Un dictionnaire python est implémenté à l'aide d'un hashmap. Par conséquent, une fois que vous convertissez un OrderedDict (combinaison d'un dict de base et d'une liste pour conserver l'ordre) en un dict, vous perdez toute information de commande. De plus, la méthode repr est censée renvoyer une chaîne qui représenterait l'objet en code python. En d'autres termes, obj == eval (repr (obj)), ou, au minimum repr (obj) == repr (eval (repr (obj))). La reproche de OrderedDict le fait très bien. dict .__ repr__ vous donnant une représentation lisible très humaine est complètement un effet secondaire du littéral dict ('{' et '}', etc.). OrderedDict n'a pas cela.
marr75
1

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

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

Il préserve l'ordre et produira presque la même chose que la réponse webwurst ( impression via json dump ).

Albert Alomar
la source
1

Pour python <3,8 (par exemple 3,6):

Patch Monkey pprint« s sortedafin de l' empêcher de tri. Cela aura l'avantage que tout fonctionne de manière récursive également, et est plus approprié que l' jsonoption pour quiconque doit utiliser par exemple le widthparamètre:

import pprint
pprint.sorted = lambda arg, *a, **kw: arg

>>> pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
{'z': 1,
 'a': 2,
 'c': {'z': 0,
       'a': 1}}

Edit: nettoyer

Pour nettoyer après cette sale affaire, lancez simplement: pprint.sorted = sorted

Pour une solution vraiment propre, vous pouvez même utiliser un contextmanager:

import pprint
import contextlib

@contextlib.contextmanager
def pprint_ordered():
    pprint.sorted = lambda arg, *args, **kwargs: arg
    yield
    pprint.sorted = sorted

# usage:

with pprint_ordered():
    pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# without it    
pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# prints: 
#    
# {'z': 1,
#  'a': 2,
#  'c': {'z': 0,
#        'a': 1}}
#
# {'a': 2,
#  'c': {'a': 1,
#        'z': 0},
#  'z': 1}
artd
la source
0

Vous pouvez redéfinir pprint()et intercepter les appels OrderedDict. Voici une illustration simple. Comme il est écrit, le OrderedDictcode de dérogation ignore toutes les options stream, indent, widthou des depthmots - clés qui peuvent avoir été transmis, mais il pourrait être amélioré pour les mettre en œuvre. Malheureusement, cette technique ne les gère pas dans un autre conteneur, comme un listof 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
# }
Martineau
la source
0

Si les éléments du dictionnaire sont tous d'un même type, vous pouvez utiliser l'étonnante bibliothèque de gestion des 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
LondresRob
la source
2
Que fera ce code? Quels avantages présente-t-il par rapport aux autres solutions ici?
Elias Zamaria