118

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.

Flimm
  • 136,138
  • 45
  • 251
  • 267
Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
  • 2
    Suggest adding a comment about ordered dictionary to http://bugs.python.org/issue7434 – Ned Deily Nov 29 '10 at 08:51
  • This has now been fixed in newer versions of Python 3. `pprint` will handle `OrderedDict` objects better. – Flimm Sep 29 '21 at 16:12
  • 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. – Flimm Sep 29 '21 at 16:15
  • If you don't want `pprint` to sort the order of keys in a `dict` object, see this question: https://stackoverflow.com/q/25683088/247696 – Flimm Sep 29 '21 at 16:16
  • 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. – Charlie Parker Oct 29 '21 at 23:48

15 Answers15

150

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
}
Flimm
  • 136,138
  • 45
  • 251
  • 267
webwurst
  • 4,830
  • 3
  • 23
  • 32
  • 1
    Alternative if order is not important: ```pprint.pprint(json.loads(json.dumps(data)))``` – scottmrogowski Mar 16 '15 at 18:07
  • 7
    @scottmrogowski Why not simply `pprint.pprint(dict(data))`? – Alfe Sep 23 '15 at 15:17
  • 3
    `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. – ws_e_c421 Sep 24 '15 at 15:54
  • 10
    @Alfe if the dict has nested OrderedDicts they won't be displayed nicely – Catskul Oct 15 '15 at 17:44
  • Doesn't necessarily work when entries in the data are datatime.datetime or datetime.time objects. – chryss Nov 16 '15 at 00:19
  • 1
    Also fails on integers as keys – DimmuR Jul 19 '16 at 08:57
  • This is the best solution for my use case; note that it is also possible to load json back into an ordered dict: http://stackoverflow.com/questions/6921699/can-i-get-json-to-load-into-an-ordereddict-in-python – hans_meine Mar 07 '17 at 13:46
  • 3
    @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. – Teekin Feb 13 '18 at 13:02
  • The question is about `OrderedDict` objects, not `dict` objects. – Flimm Sep 29 '21 at 16:00
  • 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. – Charlie Parker Oct 29 '21 at 23:48
19

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.

Flimm
  • 136,138
  • 45
  • 251
  • 267
kzh
  • 19,810
  • 13
  • 73
  • 97
  • 3
    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. – martineau Nov 29 '10 at 09:07
  • 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. – Charlie Parker Oct 29 '21 at 23:48
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)])]

Community
  • 1
  • 1
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 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. – Elias Zamaria Dec 01 '10 at 00:17
  • 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. – Charlie Parker Oct 29 '21 at 23:49
  • @Charlie: What string might that be? – martineau Oct 29 '21 at 23:55
  • @martineau the one displayed by `pprint` is a good candidate. – Charlie Parker Oct 30 '21 at 01:25
  • @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. – martineau Oct 31 '21 at 23:25
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)
]))
Ilya Prokin
  • 684
  • 6
  • 11
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'}
rumpel
  • 7,870
  • 2
  • 38
  • 39
3

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"
}
CHINTAN VADGAMA
  • 634
  • 7
  • 13
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!

flutefreak7
  • 2,321
  • 5
  • 29
  • 39
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).

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)
Christopher
  • 5,806
  • 7
  • 31
  • 41
Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
  • 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. – Elias Zamaria Nov 29 '10 at 05:38
  • -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 ^^ – martineau Nov 29 '10 at 09:33
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.

Karl Rosaen
  • 4,508
  • 2
  • 27
  • 30
  • you can also just copy the `pretty_print.py` as a local module and hack on it (removing `sorted` call or whatever you want). – Karl Rosaen Apr 05 '19 at 21:05
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

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
mxdbld
  • 16,747
  • 5
  • 34
  • 37
  • 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](https://raw.githubusercontent.com/python/cpython/master/Lib/pprint.py) 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). – MD004 Feb 19 '21 at 23:04
  • The question is about `OrderedDict` objects, and not about `dict` objects. – Flimm Sep 29 '21 at 16:09
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}
artd
  • 19
  • 2
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.

Flimm
  • 136,138
  • 45
  • 251
  • 267
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.

Ben
  • 51,770
  • 36
  • 127
  • 149
Bill M.
  • 11
  • 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. – marr75 Jun 22 '12 at 15:59
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
# }
martineau
  • 119,623
  • 25
  • 170
  • 301