Any way to properly pretty-print OrderedDict?
Asked Answered
N

15

120

I like the pprint module in Python. I use it a lot for testing and debugging. I frequently use the width option to make sure the output fits nicely within my terminal window.

It has worked fine until they added the new ordered dictionary type in Python 2.7 (another cool feature I really like). If I try to pretty-print an ordered dictionary, it doesn't show nicely. Instead of having each key-value pair on its own line, the whole thing shows up on one long line, which wraps many times and is hard to read:

>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(o)
OrderedDict([('aaaaa', 1), ('bbbbbb', 2), ('ccccccc', 3), ('dddddd', 4), ('eeeeee', 5), ('ffffff', 6), ('ggggggg', 7)])

Does anyone here have a way to make it print nicely, like the old unordered dictionaries? I could probably figure something out, possibly using the PrettyPrinter.format method, if I spend enough time, but I am wondering if anyone here already knows of a solution.

UPDATE: I filed a bug report for this. You can see it at http://bugs.python.org/issue10592.

Naphthalene answered 29/11, 2010 at 5:27 Comment(5)
Suggest adding a comment about ordered dictionary to bugs.python.org/issue7434Gagliano
This has now been fixed in newer versions of Python 3. pprint will handle OrderedDict objects better.Ostiole
It seems a lot of people are misinterpreting this question to be about dict objects (which retain insertion order in newer versions of Python), instead of about OrderedDict objects. I've modified the title to make it clearer.Ostiole
If you don't want pprint to sort the order of keys in a dict object, see this question: https://mcmap.net/q/182954/-disabling-sorting-mechanism-in-pprint-output/247696Ostiole
what I want is the string, not help printing. pprint screws up f-strings so it's useless to me. I want the string directly.Ghyll
J
152

Ever since Python 3.7, Python guarantees that keys in a dictionary will retain their insertion order. (They still don't behave exactly the same as OrderedDict objects, though, as two dicts a and b can be considered equal a == b even if the order of the keys is different, whereas OrderedDict does check this upon testing for equality.)

Python 3.8 or newer:

You can use sort_dicts=False to prevent it from sorting them alphabetically:

>>> example_dict = {'x': 1, 'b': 2, 'm': 3}
>>> import pprint
>>> pprint.pprint(example_dict, sort_dicts=False)
{'x': 1, 'b': 2, 'm': 3}

Python 3.7 or older:

As a temporary workaround, you can try dumping in JSON format instead of using pprint.

You lose some type information, but it looks nice and keeps the order.

>>> import json
>>> print(json.dumps(example_dict, indent=4))
{
    "x": 1,
    "b": 2,
    "m": 3
}
Johppah answered 4/2, 2011 at 12:33 Comment(10)
Alternative if order is not important: pprint.pprint(json.loads(json.dumps(data)))Male
@Male Why not simply pprint.pprint(dict(data))?Foam
pprint.pprint(dict(data)) works well if you don't care about the order of the keys. Personally, I wish the __repr__ for OrderedDict would produce output like this but preserve the order of the keys.Tarsuss
@Foam if the dict has nested OrderedDicts they won't be displayed nicelyDiabolic
Doesn't necessarily work when entries in the data are datatime.datetime or datetime.time objects.Canossa
Also fails on integers as keysSiege
This is the best solution for my use case; note that it is also possible to load json back into an ordered dict: #6922199Dennison
@Alfe: Because then the output is not ordered. The reason that OrderedDict is used instead of dict in the first place, is because the order matters.Sororate
The question is about OrderedDict objects, not dict objects.Ostiole
what I want is the string, not help printing. pprint screws up f-strings so it's useless to me. I want the string directly.Ghyll
F
20

The following will work if the order of your OrderedDict is an alpha sort, since pprint will sort a dict before print.

>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(dict(o.items()))
{'aaaaa': 1,
 'bbbbbb': 2,
 'ccccccc': 3,
 'dddddd': 4,
 'eeeeee': 5,
 'ffffff': 6,
 'ggggggg': 7}

Ever since Python 3.7, Python guarantees that keys in a dictionary will retain their insertion order. So if you are using Python 3.7+, you don't need to make sure that your OrderedDict is alphabetically sorted.

Foote answered 29/11, 2010 at 5:51 Comment(2)
Since OrderedDicts are ordered by insertion order, so this probably applies to a small percentage of uses. Regardless, converting the OD it to a dict should avoid the issue of everything being placed on one line.Lough
what I want is the string, not help printing. pprint screws up f-strings so it's useless to me. I want the string directly.Ghyll
L
9

Here's another answer that works by overriding and using the stock pprint() function internally. Unlike my earlier one it will handle OrderedDict's inside another container such as a list and should also be able to handle any optional keyword arguments given — however it does not have the same degree of control over the output that the other one afforded.

It operates by redirecting the stock function's output into a temporary buffer and then word wraps that before sending it on to the output stream. While the final output produced isn't exceptionalily pretty, it's decent and may be "good enough" to use as a workaround.

Update 2.0

Simplified by using standard library textwrap module, and modified to work in both 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)))]

Sample output:

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)])]

Lough answered 29/11, 2010 at 13:4 Comment(5)
I tried that and it works. As you said, it is not the prettiest, but it is the best solution I have seen so far.Naphthalene
what I want is the string, not help printing. pprint screws up f-strings so it's useless to me. I want the string directly.Ghyll
@Charlie: What string might that be?Lough
@Lough the one displayed by pprint is a good candidate.Ghyll
@Charlie: I can think of at least two ways to get that string with the code currently in my answer (as well as some of the others) — one being that the pprint() accepts a stream= keyword argument. A third way would require making a relatively trivial change to it.Lough
D
7

To print an ordered dict, e.g.

from collections import OrderedDict

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

I do

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)

Which yields

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

or

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

which yields

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))
Dossal answered 5/3, 2015 at 19:46 Comment(0)
W
5

Here’s a way that hacks the implementation of pprint. pprint sorts the keys before printing, so to preserve order, we just have to make the keys sort in the way we want.

Note that this impacts the items() function. So you might want to preserve and restore the overridden functions after doing the 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'}
Weinstock answered 24/8, 2015 at 17:26 Comment(1)
Nice, but better to subtype then override functions.Libelous
C
4

Here is my approach to pretty print an 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"
}

If you want to pretty print dictionary with keys in sorted order

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
Castellatus answered 10/8, 2018 at 20:25 Comment(1)
@AlxVallejo You might be using python3. Please checkCastellatus
C
2
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

There you go ^^

for item in li:
    pprint_od(item)

or

(pprint_od(item) for item in li)
Churchman answered 29/11, 2010 at 5:33 Comment(2)
I am looking for some way to have one function that can pretty-print OrderedDicts as well as other types. I don't see how I would use your function to pretty-print, say, a list of OrderedDicts.Naphthalene
-1 The pprint_od() function doesn't work - the for key, item in od statement results in a ValueError: too many values to unpack and the only output indented is the final " }" and the key, item in the print statement need to be in parenthesis. There you go ^^Lough
M
2

This is pretty crude, but I just needed a way to visualize a data structure made up of any arbitrary Mappings and Iterables and this is what I came up with before giving up. It's recursive, so it will fall through nested structures and lists just fine. I used the Mapping and Iterable abstract base classes from collections to handle just about anything.

I was aiming for almost yaml like output with concise python code, but didn't quite make it.

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

and some test data using OrderedDict and lists of OrderedDicts... (sheesh Python needs OrderedDict literals sooo badly...)

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)

yields the following output:

   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

I had some thoughts along the way of using str.format() for better alignment, but didn't feel like digging into it. You'd need to dynamically specify the field widths depending on the type of alignment you want, which would get either tricky or cumbersome.

Anyway, this shows me my data in readable hierarchical fashion, so that works for me!

Merchandise answered 15/2, 2014 at 7:54 Comment(0)
H
2

You can also use this simplification of the kzh answer:

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

It preserves the order and will output almost the same than the webwurst answer (print through json dump).

Hyacinthie answered 9/12, 2016 at 11:11 Comment(0)
W
2

I've tested this unholy monkey-patch based hack on python3.5 and it works:

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

You make pprint use the usual dict based summary and also disable sorting for the duration of the call so that no keys are actually sorted for printing.

Weinstein answered 5/4, 2019 at 20:12 Comment(1)
you can also just copy the pretty_print.py as a local module and hack on it (removing sorted call or whatever you want).Weinstein
U
2

As of Python 3.8 : pprint.PrettyPrinter exposes the sort_dicts keyword parameter.

True by default, setting it to False will leave the dictionary unsorted.

>>> from pprint import PrettyPrinter

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

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

Will output :

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

Reference : https://docs.python.org/3/library/pprint.html

Unearth answered 2/11, 2019 at 19:48 Comment(2)
It didn't work. Then I realized you said "as of python 3.8" which is, sadly, very true and very important ^_^. A quick workaround is to manually download the latest pprint module from a newer python version and just use it (assuming it's compatible with whatever Python version you're using - I tested with 3.6).Sorbian
The question is about OrderedDict objects, and not about dict objects.Ostiole
B
1

The pprint() method is just invoking the __repr__() method of things in it, and OrderedDict doesn't appear to do much in it's method (or doesn't have one or something).

Here's a cheap solution that should work IF YOU DON'T CARE ABOUT THE ORDER BEING VISIBLE IN THE PPRINT OUTPUT, which may be a big if:

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

I'm actually surprised that the order isn't preserved... ah well.

Biramous answered 3/2, 2012 at 16:53 Comment(1)
A python dictionary is implemented using a hashmap. Therefore, once you convert an OrderedDict (combination of a basic dict and a list to preserve order) to a dict, you lose any order information. Furthermore, the repr method is supposed to return a string that would represent the object in python code. In other words, obj == eval(repr(obj)), or, at a minimum repr(obj) == repr(eval(repr(obj))). OrderedDict's repr does this just fine. dict.__repr__ giving you a very human readable representation is completely a side effect of the dict literal ('{' and '}', etc.). OrderedDict doesn't have this.Folkways
K
1

For python < 3.8 (e.g. 3.6):

Monkey patch pprint's sorted in order to prevent it from sorting. This will have the benefit of everything working recursively as well, and is more suitable than the json option for whoever needs to use e.g. width parameter:

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: cleaning up

To clean up after this dirty business just run: pprint.sorted = sorted

For a really clean solution can even use a 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}
Kalina answered 18/3, 2020 at 10:15 Comment(1)
The question is about OrderedDict objects, and not about dict objects.Ostiole
O
1

Newer versions of Python 3 do not have the problem you are mentioning. When you pprint an OrderedDict, it does wrap each key-value pair on a separate line instead of wrapping, if there are enough key-value pairs:

>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(o)
OrderedDict([('aaaaa', 1),
             ('bbbbbb', 2),
             ('ccccccc', 3),
             ('dddddd', 4),
             ('eeeeee', 5),
             ('ffffff', 6),
             ('ggggggg', 7)])

Python 2.7 will put it all on one line, but Python 3 doesn't.

Ostiole answered 29/9, 2021 at 16:11 Comment(0)
L
0

You could redefine pprint() and intercept calls for OrderedDict's. Here's a simple illustration. As written, the OrderedDict override code ignores any optional stream, indent, width, or depth keywords that may have been passed, but could be enhanced to implement them. Unfortunately this technique doesn't handle them inside another container, such as a list of 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
# }
Lough answered 29/11, 2010 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.