Can I avoid a sorted dictionary output after I've used pprint.pprint, in Python?
Question:
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?
Answers:
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
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'}
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"
}
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'}
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}
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?
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
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'}
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"
}
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'}
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}