0

I'm using pprint to nicely print a dict and it's working fine. Now I switch to using an OrderedDict from module collections. Unfortunately, the pprint routing does not seem to recognize that such objects are more or less dicts as well and falls back to printing that as a long line.

>>> d = { i:'*'*i for i in range(8) }
>>> pprint.pprint(d)
{0: '',
 1: '*',
 2: '**',
 3: '***',
 4: '****',
 5: '*****',
 6: '******',
 7: '*******'}
>>> pprint.pprint(collections.OrderedDict(d))
OrderedDict([(0, ''), (1, '*'), (2, '**'), (3, '***'), (4, '****'), (5, '*****'), (6, '******'), (7, '*******')])

Any way to get a nicer representation of OrderedDicts as well? Maybe even if they are nested inside a normal dict or list?

Charles
  • 50,943
  • 13
  • 104
  • 142
Alfe
  • 56,346
  • 20
  • 107
  • 159

4 Answers4

3

I found a relatively simple solution for this, but it includes the risk of making the output for your ordered dictionary appear exactly as if it were a regular dict object.

The original solution for using a context manager to prevent pprint from sorting dictionary keys comes from this answer.

@contextlib.contextmanager
def pprint_OrderedDict():
    pp_orig = pprint._sorted
    od_orig = OrderedDict.__repr__
    try:
        pprint._sorted = lambda x:x
        OrderedDict.__repr__ = dict.__repr__
        yield
    finally:
        pprint._sorted = pp_orig
        OrderedDict.__repr__ = od_orig

(You could also just patch the OrderedDict.__repr__ method with dict.__repr__, but please don't.)

Example:

>>> foo = [('Roger', 'Owner'), ('Diane', 'Manager'), ('Bob', 'Manager'),
...        ('Ian', 'Associate'), ('Bill', 'Associate'), ('Melinda', 'Associate')]

>>> d = OrderedDict(foo)
>>> pprint.pprint(d)
OrderedDict([('Roger', 'Owner'), ('Diane', 'Manager'), ('Bob', 'Manager'), ('Ian', 'Associate'), ('Bill', 'Associate'), ('Melinda', 'Associate')])

>>> pprint.pprint(dict(d))
{'Bill': 'Associate',
 'Bob': 'Manager',
 'Diane': 'Manager',
 'Ian': 'Associate',
 'Melinda': 'Associate',
 'Roger': 'Owner'}

>>> with pprint_OrderedDict():
...     pprint.pprint(d)
...
{'Roger': 'Owner',
 'Diane': 'Manager',
 'Bob': 'Manager',
 'Ian': 'Associate',
 'Bill': 'Associate',
 'Melinda': 'Associate'}
Community
  • 1
  • 1
ForShame
  • 63
  • 1
  • 6
2

Try this on:

d = collections.OrderedDict({ i:'*'*i for i in range(8) })

EDIT

pprint.pprint(list(d.items()))

misakm
  • 122
  • 6
  • Yeah, well, okay. This then looks like a list of tuples, not like a `dict` anymore. I was hoping for a representation which still looked like a dict (and maybe just ignored the fact that this was an ordered one). – Alfe Jan 29 '14 at 01:28
  • 1
    I think you can even strip the `list` around those `d.items()`. But I still wonder why the `pprint` even has such a problem with `OrderedDict`s; after all they are inherting `dict`. – Alfe Jan 29 '14 at 01:30
  • Ah, I thought it didn't matter if it was in a list based on "Maybe even if they are nested inside a normal dict or list?" – misakm Jan 29 '14 at 01:34
  • No, that just meant that I also consider pretty printing things like `{ 3: OrderedDict(…) }`. – Alfe Jan 29 '14 at 01:35
  • I see. Off the top of my head I can't think of another way to do it. Why not forgo using `pprint` and loop through the OrderedDict and format the output yourself? – misakm Jan 29 '14 at 01:37
  • I'm using `pprint` to output a large tree structure, and one part deep inside that is an `OrderedDict`. – Alfe Jan 29 '14 at 01:46
  • @Alfe: That's because the implementation of `pprint` checks not only if the object is a `dict` (via `isinstance`), but also if the the class of the object (`OrderedDict` in this case) has the same `__repr__` method as `dict`. Any other classes that inherit from `dict` but do not override their `__repr__` method should be treated the same as `dict`. – musiphil Sep 18 '15 at 23:34
1

If you are specifically targeting CPython* 3.6 or later, then you can just use regular dictionaries instead of OrderedDict. You'll miss out on a few methods exclusive to OrderedDict, and this is not (yet) guaranteed to be portable to other Python implementations,** but it is probably the simplest way to accomplish what you are trying to do.

* CPython is the reference implementation of Python which may be downloaded from python.org.
** CPython stole this idea from PyPy, so you can probably depend on it working there too.

Kevin
  • 28,963
  • 9
  • 62
  • 81
  • Interesting! But currently they say it should not be relied upon until the language specs change which can happen in the future. So well, hmm. – Alfe Dec 25 '16 at 19:34
0

I realize this is sort of necroposting, but I thought I'd post what I use. Its main virtue is that its aoutput can be read back into python, thus allowing, for instance, to shutlle between representations (which I use, for instance, on JSON files). Of course it breaks pprint encapsulation, by ripping some code off its inner _format function.

#!/bin/env python
from __future__ import print_function

import pprint;
from collections import OrderedDict
import json
import sys


class MyPP (pprint.PrettyPrinter):
    def _format(self, object, stream, indent, allowance, context, level):
        if not isinstance(object, OrderedDict) :
            return pprint.PrettyPrinter._format(self, object, stream, indent, allowance, context, level)
        level = level + 1
        objid = id(object)
        if objid in context:
            stream.write(_recursion(object))
            self._recursive = True
            self._readable = False
            return
        write = stream.write
        _len=len
        rep = self._repr(object, context, level - 1)
        typ = type(object)
        sepLines = _len(rep) > (self._width - 1 - indent - allowance)

        if self._depth and level > self._depth:
            write(rep)
            return

        write('OrderedDict([\n%s'%(' '*(indent+1),))
        if self._indent_per_level > 1:
            write((self._indent_per_level - 1) * ' ')
        length = _len(object)
        #import pdb; pdb.set_trace()
        if length:
            context[objid] = 1
            indent = indent + self._indent_per_level
            items = object.items()
            key, ent = items[0]
            rep = self._repr(key, context, level)
            write('( ')
            write(rep)
            write(', ')
            self._format(ent, stream, indent + _len(rep) + 2,
                         allowance + 1, context, level)
            write(' )')
            if length > 1:
                for key, ent in items[1:]:
                    rep = self._repr(key, context, level)
                    if sepLines:
                        write(',\n%s( %s , ' % (' '*indent, rep))
                    else:
                        write(', ( %s , ' % rep)
                    self._format(ent, stream, indent + _len(rep) + 2,
                                 allowance + 1, context, level)
                    write(' )')

            indent = indent - self._indent_per_level
            del context[objid]
        write('])')
        return

pp = MyPP(indent=1)
handle=open(sys.argv[1],"r")
values=json.loads(handle.read(),object_pairs_hook=OrderedDict)
pp.pprint(values)
Alien Life Form
  • 1,884
  • 1
  • 19
  • 27