15

The code is:

from pprint import pprint
d = {"b" : "Maria", "c" : "Helen", "a" : "George"}
pprint(d, width = 1)

The output is:

{'a': 'George',
'b': 'Maria',
'c': 'Helen'}

But, the desired output is:

{'b': 'Maria',
'c': 'Helen',
'a': 'George'}

Could this be done with pprint or is there another way?

Maria Pantsiou
  • 173
  • 1
  • 6
  • 2
    `OrderedDict` or use a function to sort over keys and print them individually. But as another note, why do you care if a dictionary has an order? – Francisco Aug 10 '18 at 14:12
  • 2
    https://docs.python.org/3/library/pprint.html says, "Dictionaries are sorted by key before the display is computed." Doesn't make it sound like it's optional. – Kevin Aug 10 '18 at 14:13
  • 1
    @EdChum, @Francisco, sure, OrderedDict will display the results in ordered order. But if you pass one to pprint, it won't be "pretty": it will look like `OrderedDict([items go here])`. Doesn't look much like the desired output. – Kevin Aug 10 '18 at 14:15
  • @Kevin I just noticed that bit, you're correct this is unavoidable with `pprint` so the OP needs to consider a different metho – EdChum Aug 10 '18 at 14:16
  • You might be able to subclass `PrettyPrinter` and override `pformat` to get your desired output. I'm not sure though – Patrick Haugh Aug 10 '18 at 14:18
  • @Kevin, Sure, but if I use a simple print, then I take an unsorted result, as I want. The problem with that is that I have to print the dict exactly as the output shows above, but simple print prints everything in one line, so I can't use it. – Maria Pantsiou Aug 10 '18 at 14:21

5 Answers5

20

Python 3.8 or newer:

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

pprint.pprint(data, sort_dicts=False)

Python 3.7 or older:

Since Python 3.7 (or 3.6 in the case of cPython), dict preserves insertion order. For any version prior, you will need to use an OrderedDict to keep keys in order.

Although, from the doc on pprint:

Dictionaries are sorted by key before the display is computed.

This means pprint will break your desired order regardless.

Alternative method:

You can also use json.dumps to pretty print your data.

Code:

import json
from collections import OrderedDict

# For Python 3.6 and prior, use an OrderedDict
d = OrderedDict(b="Maria", c="Helen", a="George")

print(json.dumps(d, indent=1))

Output:

{
 "b": "Maria",
 "c": "Helen",
 "a": "George"
}
Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
4

If you read the source of pprint.py you'll find that in PrettyPrinter._pprint_dict(), the method responsible for formatting dicts:

def _pprint_dict(self, object, stream, indent, allowance, context, level):
    write = stream.write
    write('{')
    if self._indent_per_level > 1:
        write((self._indent_per_level - 1) * ' ')
    length = len(object)
    if length:
        items = sorted(object.items(), key=_safe_tuple)
        self._format_dict_items(items, stream, indent, allowance + 1,
                                context, level)
    write('}')

_dispatch[dict.__repr__] = _pprint_dict

There's this line items = sorted(object.items(), key=_safe_tuple), so dict items are always sorted first before being processed for formatting, and you will have to override it yourself by copying and pasting it and removing the offending line in your own script:

import pprint as pp
def _pprint_dict(self, object, stream, indent, allowance, context, level):
    write = stream.write
    write('{')
    if self._indent_per_level > 1:
        write((self._indent_per_level - 1) * ' ')
    length = len(object)
    if length:
        self._format_dict_items(object.items(), stream, indent, allowance + 1,
                                context, level)
    write('}')
pp.PrettyPrinter._dispatch[dict.__repr__] = _pprint_dict

so that:

pp.pprint({"b" : "Maria", "c" : "Helen", "a" : "George"}, width=1)

will output (in Python 3.6+):

{'b': 'Maria',
 'c': 'Helen',
 'a': 'George'}
blhsing
  • 91,368
  • 6
  • 71
  • 106
  • The issue I encountered when subclassing is that the private methods are called only when the standard representation exceeds the width. While you overcame the this problem by setting width=1, a general solution would require also overriding the PrettyPrinter.format method. – Olivier Melançon Aug 10 '18 at 17:56
  • Indeed. I've added a new answer with a more generic approach. – blhsing Apr 13 '19 at 00:59
4

As of Python 3.8, pprint has a sort_dicts keyword argument you can set to prevent it from sorting dictionary keys alphabetically. If you set it to false, it will use the default ordering of dictionaries (e.g. insertion order).

Example:

>>> from pprint import pprint
>>> pprint(dict(z=0, a=1), sort_dicts=False)
{'z': 0, 'a': 1}
Edward D'Souza
  • 2,453
  • 1
  • 26
  • 24
3

A more generic solution is to use unittest.mock.patch to override the built-in sorted function with a function that does nothing but return the given first argument:

import pprint
from unittest.mock import patch

def unsorted_pprint(*args, **kwargs):
    with patch('builtins.sorted', new=lambda l, **_: l):
        orig_pprint(*args, **kwargs)

orig_pprint = pprint.pprint
pprint.pprint = unsorted_pprint

so that:

pprint.pprint({"b" : "Maria", "c" : "Helen", "a" : "George"})

outputs:

{'b': 'Maria', 'c': 'Helen', 'a': 'George'}
blhsing
  • 91,368
  • 6
  • 71
  • 106
2

You should use OrderedDict from the collections library of python to keep the ordering constant

from collections import OrderedDict
from pprint import pprint
d = OrderedDict({"b" : "Maria", "c" : "Helen", "a" : "George"})
pprint(d, width = 1)

UPDATE:

Since output is important, you can use the following code, its a hack but you create a function to implement this feature:

from collections import OrderedDict
d = OrderedDict({"b" : "Maria", "c" : "Helen", "a" : "George"})
print('{', end='')
total_len = len(d)
current_index = 1
for key, value in d.items():
    print('\''+key+'\': \'' + value+ '\'', end='')
    if current_index<total_len:
        print(',')
    else:
        print('}')
    current_index += 1
Arghya Saha
  • 5,599
  • 4
  • 26
  • 48