Disabling sorting mechanism in pprint output

Question:

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

Asked By: Edd

||

Answers:

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})
Answered By: Robᵩ

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)
Answered By: theY4Kman

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.

Answered By: Norrius

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.

Answered By: Harsh Parekh

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.

Answered By: wim