有没有正确漂亮地打印OrderedDict的方法?

119
我喜欢Python中的pprint模块。我经常用它来进行测试和调试。我经常使用width选项确保输出适合我的终端窗口。
这一直很好,直到他们在Python 2.7中添加了新的ordered dictionary type(另一个我非常喜欢的很酷的功能)。如果我尝试漂亮地打印有序字典,它就不会显示得很好。整个东西不是每个键值对都在自己的行上,而是在一条长线上显示,这条长线会多次换行,很难阅读:
>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(o)
OrderedDict([('aaaaa', 1), ('bbbbbb', 2), ('ccccccc', 3), ('dddddd', 4), ('eeeeee', 5), ('ffffff', 6), ('ggggggg', 7)])

这里有人知道如何使它像旧的无序字典一样打印得好看吗?如果我花足够的时间,可能可以使用PrettyPrinter.format方法来解决,但我想知道这里是否已经有人知道解决方案。

更新:我为此提交了一个错误报告。您可以在http://bugs.python.org/issue10592中查看它。


2
建议在 http://bugs.python.org/issue7434 中添加有关有序字典的注释。 - Ned Deily
这个问题现在已经在更新的Python 3版本中得到了修复。pprint将更好地处理OrderedDict对象。 - Flimm
很多人似乎误解了这个问题是关于dict对象的(在Python的新版本中保留插入顺序),而不是关于OrderedDict对象的。我修改了标题以使其更清晰。 - Flimm
如果您不想让 pprintdict 对象中的键进行排序,请参阅此问题:https://dev59.com/BF8e5IYBdhLWcg3wfajv - Flimm
我想要的是字符串,而不是打印帮助。pprint会破坏f-strings,所以对我来说毫无用处。我直接想要字符串。 - Charlie Parker
15个回答

2
截至Python 3.8版本:pprint.PrettyPrinter暴露了sort_dicts关键字参数。默认为True,将其设置为False将使字典保持未排序状态。
>>> from pprint import PrettyPrinter

>>> x = {'John': 1,
>>>      'Mary': 2,
>>>      'Paul': 3,
>>>      'Lisa': 4,
>>>      }

>>> PrettyPrinter(sort_dicts=False).pprint(x)

将输出:

{'John': 1, 
 'Mary': 2, 
 'Paul': 3,
 'Lisa': 4}

参考文献: https://docs.python.org/3/library/pprint.html

它没起作用。然后我意识到你说的是“截至Python 3.8”,这非常真实和重要^_^。一个快速的解决方法是手动下载最新的pprint模块(https://raw.githubusercontent.com/python/cpython/master/Lib/pprint.py)并使用它(假设它与您正在使用的Python版本兼容--我已在3.6上测试)。 - MD004
问题是关于OrderedDict对象,而不是dict对象。 - Flimm

1

对于 Python < 3.8(例如 3.6):

猴子补丁 pprintsorted,以防止其排序。 这将有递归工作的好处,并且比需要使用例如 width 参数的 json 选项更合适:

import pprint
pprint.sorted = lambda arg, *a, **kw: arg

>>> pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
{'z': 1,
 'a': 2,
 'c': {'z': 0,
       'a': 1}}

编辑:清理

要在这个脏乱的业务之后进行清理,只需运行: pprint.sorted = sorted

对于一个真正干净的解决方案,甚至可以使用上下文管理器:

import pprint
import contextlib

@contextlib.contextmanager
def pprint_ordered():
    pprint.sorted = lambda arg, *args, **kwargs: arg
    yield
    pprint.sorted = sorted

# usage:

with pprint_ordered():
    pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# without it    
pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)

# prints: 
#    
# {'z': 1,
#  'a': 2,
#  'c': {'z': 0,
#        'a': 1}}
#
# {'a': 2,
#  'c': {'a': 1,
#        'z': 0},
#  'z': 1}

问题是关于OrderedDict对象,而不是dict对象。 - Flimm

1

pprint() 方法只是调用其中的内容的 __repr__() 方法,而 OrderedDict 在其方法中似乎没有做太多事情(或者根本没有方法之类的东西)。

这里有一个简单的解决方案,如果您不关心顺序在 PPRINT 输出中是否可见,那么它应该可以工作,但这可能是一个大问题:

class PrintableOrderedDict(OrderedDict):
    def __repr__(self):
        return dict.__repr__(self)

我其实很惊讶顺序没有被保留...哎呀。


使用哈希映射实现 Python 字典。因此,一旦将 OrderedDict(基本字典和列表的组合来保留顺序)转换为字典,就会丢失任何顺序信息。此外,repr 方法应返回一个字符串,该字符串将表示 Python 代码中的对象。换句话说,obj == eval(repr(obj)),或者至少 repr(obj) == repr(eval(repr(obj)))。OrderedDict 的 repr 就做得很好。dict.repr 提供的非常人性化可读的表示完全是字典文字('{' 和 '}'等)的副作用。OrderedDict 没有这个。 - marr75

1
较新版本的Python 3不存在您提到的问题。当您使用pprint打印一个OrderedDict时,它会将每个键值对分别放在一行上而不是在同一行上,如果有足够多的键值对:
>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(o)
OrderedDict([('aaaaa', 1),
             ('bbbbbb', 2),
             ('ccccccc', 3),
             ('dddddd', 4),
             ('eeeeee', 5),
             ('ffffff', 6),
             ('ggggggg', 7)])

Python 2.7会将所有内容放在一行上,但Python 3不会。

0

您可以重新定义pprint()并拦截对OrderedDict的调用。这里是一个简单的示例。按照现有的方式,OrderedDict覆盖代码会忽略任何可选的streamindentwidthdepth关键字,但可以增强以实现它们。不幸的是,这种技术无法处理它们在另一个容器内部,例如OrderDict列表。

from collections import OrderedDict
from pprint import pprint as pp_pprint

def pprint(obj, *args, **kwrds):
    if not isinstance(obj, OrderedDict):
        # use stock function
        return pp_pprint(obj, *args, **kwrds)
    else:
        # very simple sample custom implementation...
        print "{"
        for key in obj:
            print "    %r:%r" % (key, obj[key])
        print "}"

l = [10, 2, 4]
d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
pprint(l, width=4)
# [10,
#  2,
#  4]
pprint(d)
# {'john': 1, 'mary': 3, 'paul': 2}

pprint(od)
# {
#     'john':1
#     'paul':2
#     'mary':3
# }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接