禁用pprint输出中的排序机制

41 投票
6 回答
14190 浏览
提问于 2025-04-20 00:08

我有一个很大的字典,我想用prettyprint来打印出来好看一点。但是我该怎么做才能保持格式,同时又不让prettyprint自动排序呢?

6 个回答

0

我知道我来得有点晚,但我喜欢用 partial 来禁用字典的排序:

from functools import partial
from pprint import pprint

pprint = partial(pprint, sort_dicts=False)

我个人觉得这种方法不错,因为它的改动最小。

这个方法的好处在于,你只需要在一个地方进行修改(和其他选项不同),而不需要去改动 pprint 的内部实现。

我用的是 Python 3.8,但从添加 sort_dicts 选项的版本开始,这个方法应该都能用。

1

你可以创建一个新的类,继承自 PrettyPrinter,然后在 _pprint_dict 这个方法里去掉 sorted(object.items()) 这一行代码。

注意:这段代码适用于 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)
6

与其使用 pprint.pprint,不如省去4个字符,直接用 pprint.pp,这个方法不会对字典进行排序:

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

这个方法会打印出 object 的格式化表示,并在后面加一个换行。如果 sort_dicts 设置为假(默认就是假),那么字典里的键会按照插入的顺序显示;如果设置为真,字典的键就会被排序。argskwargs 会作为格式化参数传递给 pprint()

这个功能在3.8版本中新增。

18

从Python 3.8开始,你可以通过设置 sort_dicts=False 来关闭字典的排序功能。需要注意的是,从Python 3.7开始,字典是按照插入顺序排列的(实际上,从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}

另外,你可以创建一个 漂亮打印对象

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

不过,这个设置不会影响集合(sets),集合依然是排序的,但集合本身并没有插入顺序的保证。

43

Python 3.8 或更新版本:

使用 sort_dicts=False

pprint.pprint(data, sort_dicts=False)

Python 3.7 或更早版本:

你可以通过猴子补丁的方式来修改 pprint 模块。

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})

因为第二个输出基本上是随机排序的,所以你的输出可能和我的不一样:

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

还有一个更复杂但更易用的版本:
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})

撰写回答