有没有办法正确美化打印OrderedDict?
我很喜欢Python里的pprint模块。这个模块在我测试和调试的时候用得很多。我经常使用它的宽度选项,确保输出的内容能很好地适应我的终端窗口。
这个模块一直都很好用,直到他们在Python 2.7中添加了新的有序字典类型(我也很喜欢这个新特性)。如果我尝试对一个有序字典进行美化打印,它的显示效果就不好了。每个键值对没有单独占一行,而是全部挤在一行上,变成一长串,换行很多次,读起来很费劲:
>>> 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方法,如果我花足够的时间去研究,但我想知道这里有没有人已经知道解决方案。
更新:我已经提交了一个bug报告。你可以在这里查看:http://bugs.python.org/issue10592。
15 个回答
这里有另一个方法,它通过重写并在内部使用标准的 pprint()
函数来实现。与我之前的方法不同,这个方法可以处理在其他容器(比如 list
)中的 OrderedDict
,而且也能处理任何可选的关键字参数。不过,它对输出的控制程度没有之前的方法那么高。
这个方法的工作原理是把标准函数的输出重定向到一个临时的缓冲区,然后在发送到输出流之前进行换行处理。虽然最终生成的输出看起来不是特别好,但还算可以,可能“足够好”用作临时解决方案。
更新 2.0
通过使用标准库中的 textwrap
模块进行了简化,并修改为可以在 Python 2 和 3 中使用。
from collections import OrderedDict
try:
from cStringIO import StringIO
except ImportError: # Python 3
from io import StringIO
from pprint import pprint as pp_pprint
import sys
import textwrap
def pprint(object, **kwrds):
try:
width = kwrds['width']
except KeyError: # unlimited, use stock function
pp_pprint(object, **kwrds)
return
buffer = StringIO()
stream = kwrds.get('stream', sys.stdout)
kwrds.update({'stream': buffer})
pp_pprint(object, **kwrds)
words = buffer.getvalue().split()
buffer.close()
# word wrap output onto multiple lines <= width characters
try:
print >> stream, textwrap.fill(' '.join(words), width=width)
except TypeError: # Python 3
print(textwrap.fill(' '.join(words), width=width), file=stream)
d = dict((('john',1), ('paul',2), ('mary',3)))
od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
lod = [OrderedDict((('john',1), ('paul',2), ('mary',3))),
OrderedDict((('moe',1), ('curly',2), ('larry',3))),
OrderedDict((('weapons',1), ('mass',2), ('destruction',3)))]
示例输出:
pprint(d, width=40)
» {'john': 1, 'mary': 3, 'paul': 2}
pprint(od, width=40)
» OrderedDict([('john', 1), ('paul', 2),
('mary', 3)])
pprint(lod, width=40)
» [OrderedDict([('john', 1), ('paul', 2),
('mary', 3)]), OrderedDict([('moe', 1),
('curly', 2), ('larry', 3)]),
OrderedDict([('weapons', 1), ('mass',
2), ('destruction', 3)])]
如果你的OrderedDict里的内容是按字母顺序排列的,下面的代码就能正常工作,因为pprint在打印之前会对字典进行排序。
>>> from collections import OrderedDict
>>> o = OrderedDict([("aaaaa", 1), ("bbbbbb", 2), ("ccccccc", 3), ("dddddd", 4), ("eeeeee", 5), ("ffffff", 6), ("ggggggg", 7)])
>>> import pprint
>>> pprint.pprint(dict(o.items()))
{'aaaaa': 1,
'bbbbbb': 2,
'ccccccc': 3,
'dddddd': 4,
'eeeeee': 5,
'ffffff': 6,
'ggggggg': 7}
从Python 3.7开始,Python保证字典里的键会保持你添加的顺序。所以如果你使用的是Python 3.7或更高版本,就不需要再确保你的OrderedDict是按字母顺序排列的了。
从Python 3.7开始,Python保证字典里的键会按照你添加的顺序保留。虽然它们的行为和OrderedDict
对象不完全一样,比如两个字典a
和b
即使键的顺序不同,也可以被认为是相等的a == b
,而OrderedDict
在比较相等性时会考虑顺序。
Python 3.8或更新版本:
你可以使用sort_dicts=False
来防止字典按字母顺序排序:
>>> example_dict = {'x': 1, 'b': 2, 'm': 3}
>>> import pprint
>>> pprint.pprint(example_dict, sort_dicts=False)
{'x': 1, 'b': 2, 'm': 3}
Python 3.7或更早版本:
作为一个临时解决办法,你可以尝试用JSON格式输出,而不是使用pprint
。
这样你会失去一些类型信息,但看起来不错,而且能保持顺序。
>>> import json
>>> print(json.dumps(example_dict, indent=4))
{
"x": 1,
"b": 2,
"m": 3
}