Disabling sorting mechanism in pprint output
Asked Answered
F

6

41

I have big dictionary which I`m printing for viewing with prettyprint, but how I can keep formatting but kill sorting mechanism in pprint?

Faison answered 5/9, 2014 at 9:50 Comment(0)
H
43

Python 3.8 or newer:

Use sort_dicts=False:

pprint.pprint(data, sort_dicts=False)

Python 3.7 or older:

You can monkey patch the pprint module.

import pprint

pprint.pprint({"def":2,"ghi":3,"abc":1,})
pprint._sorted = lambda x:x
# Or, for Python 3.7:
# pprint.sorted = lambda x, key=None: x
pprint.pprint({"def":2,"ghi":3, "abc":1})

Since the 2nd output is essentiallly randomly sorted, your output may be different from mine:

{'abc': 1, 'def': 2, 'ghi': 3}
{'abc': 1, 'ghi': 3, 'def': 2}

Another version that is more complex, but easier to use:
import pprint
import contextlib

@contextlib.contextmanager
def pprint_nosort():
    # Note: the pprint implementation changed somewhere
    # between 2.7.12 and 3.7.0. This is the danger of
    # monkeypatching!
    try:
        # Old pprint
        orig,pprint._sorted = pprint._sorted, lambda x:x
    except AttributeError:
        # New pprint
        import builtins
        orig,pprint.sorted = None, lambda x, key=None:x

    try:
        yield
    finally:
        if orig:
            pprint._sorted = orig
        else:
            del pprint.sorted

# For times when you don't want sorted output
with pprint_nosort():
    pprint.pprint({"def":2,"ghi":3, "abc":1})

# For times when you do want sorted output
pprint.pprint({"def":2,"ghi":3, "abc":1})
Humperdinck answered 5/9, 2014 at 14:40 Comment(7)
This no longer works unfortunately, pprint uses the sorted built-in everywhereHeartsome
Thanks, @Heartsome - I've updated the answer to include support for, at the least, 3.7.0.Phelan
key=None y assigning?Local
@SmartManoj - that's not an assignment, it is a default value of the key parameter in that lambda. I don't recall anymore why that parameter has a default value.Phelan
I think the default value is not neededLocal
I think the default value is not neededLocal
Does not work for nested dictsImparisyllabic
E
18

As of Python 3.8, you can finally disable this using sort_dicts=False. Note that dictionaries are insertion-ordered since Python 3.7 (and in practice, even since 3.6).

import pprint

data = {'not': 'sorted', 'awesome': 'dict', 'z': 3, 'y': 2, 'x': 1}
pprint.pprint(data, sort_dicts=False)
# prints {'not': 'sorted', 'awesome': 'dict', 'z': 3, 'y': 2, 'x': 1}

Alternatively, create a pretty printer object:

pp = pprint.PrettyPrinter(sort_dicts=False)
pp.pprint(data)

This does not affect sets (which are still sorted), but then sets do not have insertion-ordering guarantees.

Electricity answered 1/12, 2019 at 17:27 Comment(1)
Does not work with nested dicts. The order of the nested dicts is printed out of order.Imparisyllabic
S
6

Instead of pprint.pprint, save yourself 4 characters and use pprint.pp, which doesn't sort:

pprint.pp(object, *args, sort_dicts=False, **kwargs)

Prints the formatted representation of object followed by a newline. If sort_dicts is false (the default), dictionaries will be displayed with their keys in insertion order, otherwise the dict keys will be sorted. args and kwargs will be passed to pprint() as formatting parameters.

New in version 3.8.

Sochor answered 24/5, 2022 at 21:53 Comment(1)
This works for nested dicts, while sort_dicts=False does not.Imparisyllabic
S
1

You can subclass PrettyPrinter and remove the sorted(object.items()) from _pprint_dict.

NOTE: this code is Python 3.5+

# unsorted_pprint.py

from pprint import PrettyPrinter, _builtin_scalars, _recursion

__all__ = [
    'UnsortedPrettyPrinter',
    'pprint2',
    'pformat2',
]


class UnsortedPrettyPrinter(PrettyPrinter):
    """Pretty printer that retains original dict ordering
    """
    def __init__(self, *args, **kwargs):
        super().__init__()

        self._dispatch = {
            **self._dispatch,
            dict.__repr__: self._pprint_dict,
        }

    @staticmethod
    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 = object.items()
            self._format_dict_items(items, stream, indent, allowance + 1,
                                    context, level)
        write('}')

    def format(self, object, context, maxlevels, level):
        """Format object for a specific context, returning a string
        and flags indicating whether the representation is 'readable'
        and whether the object represents a recursive construct.
        """
        return self._safe_repr(object, context, maxlevels, level)

    def _safe_repr(self, object, context, maxlevels, level):
        typ = type(object)
        if typ in _builtin_scalars:
            return repr(object), True, False

        r = getattr(typ, "__repr__", None)
        if issubclass(typ, dict) and r is dict.__repr__:
            if not object:
                return "{}", True, False
            objid = id(object)
            if maxlevels and level >= maxlevels:
                return "{...}", False, objid in context
            if objid in context:
                return _recursion(object), False, True
            context[objid] = 1
            readable = True
            recursive = False
            components = []
            append = components.append
            level += 1
            saferepr = self._safe_repr
            items = object.items()
            for k, v in items:
                krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
                vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
                append("%s: %s" % (krepr, vrepr))
                readable = readable and kreadable and vreadable
                if krecur or vrecur:
                    recursive = True
            del context[objid]
            return "{%s}" % ", ".join(components), readable, recursive

        if (issubclass(typ, list) and r is list.__repr__) or \
            (issubclass(typ, tuple) and r is tuple.__repr__):
            if issubclass(typ, list):
                if not object:
                    return "[]", True, False
                format = "[%s]"
            elif len(object) == 1:
                format = "(%s,)"
            else:
                if not object:
                    return "()", True, False
                format = "(%s)"
            objid = id(object)
            if maxlevels and level >= maxlevels:
                return format % "...", False, objid in context
            if objid in context:
                return _recursion(object), False, True
            context[objid] = 1
            readable = True
            recursive = False
            components = []
            append = components.append
            level += 1
            for o in object:
                orepr, oreadable, orecur = self._safe_repr(o, context, maxlevels, level)
                append(orepr)
                if not oreadable:
                    readable = False
                if orecur:
                    recursive = True
            del context[objid]
            return format % ", ".join(components), readable, recursive

        rep = repr(object)
        return rep, (rep and not rep.startswith('<')), False


def pprint2(object, stream=None, indent=1, width=80, depth=None, *,
           compact=False):
    """Pretty-print a Python object to a stream [default is sys.stdout].

    dict items are left unsorted.
    """
    printer = UnsortedPrettyPrinter(
        stream=stream,
        indent=indent,
        width=width,
        depth=depth,
        compact=compact,
    )
    printer.pprint(object)


def pformat2(object, indent=1, width=80, depth=None, *, compact=False):
    """Format a Python object into a pretty-printed representation.

    dict items are left unsorted.
    """
    return UnsortedPrettyPrinter(
        indent=indent,
        width=width,
        depth=depth,
        compact=compact,
    ).pformat(object)
Soper answered 21/6, 2019 at 22:56 Comment(0)
S
0

I know that I am a bit late here but my preferred way to disable sorting dicts is by using partial:

from functools import partial
from pprint import pprint

pprint = partial(pprint, sort_dicts=False)

Personally I like this way as it introduces the smallest diffs.

It has the benefit of monkey patching in that you only have to make changes at one spot(unlike other options) without having to mess with the internals of pprint.

I'm using py3.8 but this should work from whenever sort_dicts option was added.

Scatology answered 31/8, 2020 at 19:19 Comment(0)
N
0

You can use json.dumps with indent=4. This creates a formatted string, that can be printed nicely and does not sort keys by default.

Note: this works also for old pythons

import json
d = {3:4, 1:2, 5:6}
print(json.dumps(d, indent=4))

output:

{
    "3": 4,
    "1": 2,
    "5": 6
}
Nordin answered 10/1, 2023 at 14:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.