如何美观地打印嵌套字典?

547
我该如何在Python中美化输出一个大约深度为4的字典?我试着用 pprint() 进行美化输出,但它没有起作用:
import pprint 
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(mydict)

我只需要每个嵌套都有一个缩进("\t"),这样我就可以得到像这样的结果:
key1
    value1
    value2
    key2
       value1
       value2

我该怎么做?

等等。


47
“did not work” 的意思是“没有起作用”。请非常精确地说明pprint“没有起作用”的具体情况。 - S.Lott
8
我现在已经使用了这三个答案(每个都适用于特定场景):@Ken的json答案很好,但有时在对象无法进行json序列化(抛出异常)时会失败。如果@Ken的json答案不起作用,请尝试@Andy的yaml答案,它应该可以工作,但是字符串输出不太容易读懂。[@sth的答案]是最通用的(适用于任何对象,并且不使用任何库)。 - Trevor Boyd Smith
我认为你应该尝试找到一个合适的 width 参数。请查看说明文档 - Ersel Er
pretty print 有什么问题吗?import pprint.pprint as pprint;pprint(d)? - Charlie Parker
能否获取漂亮打印字符串的字符串表示形式?即,我想要漂亮可打印的字符串,因此不希望直接打印的解决方案。 - Charlie Parker
你是否只是缺少了width=1选项?https://dev59.com/zGIj5IYBdhLWcg3whFa4#75119001 - JayRizzo
30个回答

843

我最初的想法是JSON序列化器可能非常擅长嵌套字典,所以我想采用这种方式来解决:

>>> import json
>>> print(json.dumps({'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}},
...                  sort_keys=True, indent=4))
{
    "a": 2,
    "b": {
        "x": 3,
        "y": {
            "t1": 4,
            "t2": 5
        }
    }
}

79
很酷,但无法很好地打印所有字典。打印json.dumps(myObject.dict, sort_keys=True, indent=4)会出现TypeError: <object at 0x0000000002E6A748> is not JSON serializable的错误提示。 - tponthieux
12
虽然这看起来很有用,但输出结果并不是原帖作者想要的。 - martineau
8
楼上的 @martineau 说,楼主要求的输出没有意义,因为字典需要根据值来确定键。 - naught101
3
一个漂亮的打印机可以执行任何必要的操作,以产生期望的输出。 - martineau
51
json.dumps可以接受一个转换函数作为可选参数,因此使用json.dumps(myObject.dict, sort_keys=True, indent=4, default=str),您至少可以使用对象的__repr__实现来打印对象本身,并解决“不可JSON序列化”的TypeError问题。 - RFairey
显示剩余13条评论

220

我不确定您想要的格式是什么样子的,但您可以从以下此函数开始:

def pretty(d, indent=0):
   for key, value in d.items():
      print('\t' * indent + str(key))
      if isinstance(value, dict):
         pretty(value, indent+1)
      else:
         print('\t' * (indent+1) + str(value))

17
你知道@Ken的常规回答比这个好得多。Json已经处理了一切,而这种方法可能会出现错误,如:**UnicodeEncodeError: 'ascii' codec can't encode character u'\xf3' in position 50: ordinal not in range(128)**。 - wonderwhy
很不错。如果你有像OP问题中的嵌套列表,你需要添加一些处理。如果你在Py2中遇到问题,那是因为它不能正确处理Unicode,除非使用像答案中提到的__future__这样的hack,所以你必须在需要的地方采用这些方法(或者升级到3)。 - sudo
2
这对我来说运行得很好:def pretty(d, indent=0): for key, value in d.items(): if isinstance(value, dict): print(' ' * indent + str(key)) pretty(value, indent+1) else: print(' ' * (indent+1) + f"{key}: {value}") - hum3
1
如果想要为了可读性限制输出量,可以考虑使用IPython或者我的prettyformatter - Simply Beautiful Art
5
“Json already handles everything”并不是完全正确的,例如dumps({(2, 4): 3}, indent=2)会报错 "TypeError: keys must be str, int, float, bool or None, not tuple"。而dumps({3: 3}, indent=2)虽然有输出,但它错误地将键打印为字符串。此外,还有其他数据类型(如datetime)需要特殊处理。 - Arthur Tacca
显示剩余4条评论

110

您可以尝试使用YAML,通过PyYAML实现。 它的输出可以进行微调。 我建议从以下内容开始:

print(yaml.dump(data, allow_unicode=True, default_flow_style=False))
结果非常易读;如果需要,也可以将其解析回Python代码。
编辑: 示例:
>>> import yaml
>>> data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> print(yaml.dump(data, default_flow_style=False))
a: 2
b:
  x: 3
  y:
    t1: 4
    t2: 5

2
使用yaml非常有趣,因为它在格式上保留了数据类型,唯一可以针对它说的是它不生成有效的Python字符串,但几乎可以转换回Python。 - y.petremann
2
YAML 不支持 Numpy 的标量类型版本... 我并不惊讶它不支持 numpy 数组,但我本来期望 floatnumpy.float64 有相同的输出结果。 - PhilMacKay
1
这种方法对我也适用,使用一个字典列表。 - Grant Shannon
有可能获得漂亮打印字符串的字符串表示形式吗?即我想要漂亮可打印的字符串,因此不想要直接打印的解决方案。 - Charlie Parker
@CharlieParker 只是不要调用 print() 函数? - Andy Mikhaylenko

98

其中一种最pythonic的方法是使用已经构建好的pprint模块。

你需要定义打印深度的参数就像你想象的那样depth

import pprint
pp = pprint.PrettyPrinter(depth=4)
pp.pprint(mydict)

就是这样!


能否获取漂亮打印字符串的字符串表示形式?即,我想要漂亮可打印的字符串,因此不希望直接打印的解决方案。 - Charlie Parker
@CharlieParker,如果您点击上面答案中的文档链接,您将会发现还有一个名为pprint.pformat()的方法。 - wovano
2
我认为这不符合 OP 的要求,或者至少对我来说不起作用: >>> pprint.PrettyPrinter(depth=4).pprint({'a': 2, 'b': {'x': 3, 'y': {'t1': 4, 't2': 5}}}) 的结果是 {'a': 2, 'b': {'x': 3, 'y': {'t1': 4, 't2': 5}}}(一行)。 - jjj
2
我不认为这个做了OP要求的事情,或者至少对我来说没有起作用: >>> pprint.PrettyPrinter(depth=4).pprint({'a': 2, 'b': {'x': 3, 'y': {'t1': 4, 't2': 5}}}) 的结果是 {'a': 2, 'b': {'x': 3, 'y': {'t1': 4, 't2': 5}}}(全部在一行上)。 - undefined

93

通过这种方式,您可以漂亮地打印它,例如您的字典名称是yasin

import json

print (json.dumps(yasin, indent=2))

或者,更安全的方法:

print (json.dumps(yasin, indent=2, default=str))

18
假定字典的内容可以进行 JSON 序列化,但这并非必然成立。 - SpiXel
5
@SpiXel。我喜欢Juan-Kabbali的回答...但是为了回答你的问题,可以这样做:print(json.dumps(yasin, indent=2, default=str)):任何“棘手”的情况都可以用str函数处理。 - mike rodent
这解决了我的问题,“类型为User的对象不可JSON序列化”。 - avirr
能否获取漂亮打印字符串的字符串表示形式?即我想要漂亮可打印的字符串,因此不希望直接打印的解决方案。 - Charlie Parker

48

就目前而言,我没有看到任何一个漂亮的打印机能够至少模仿Python解释器的输出并进行非常简单的格式化,因此这是我的实现:

class Formatter(object):
    def __init__(self):
        self.types = {}
        self.htchar = '\t'
        self.lfchar = '\n'
        self.indent = 0
        self.set_formater(object, self.__class__.format_object)
        self.set_formater(dict, self.__class__.format_dict)
        self.set_formater(list, self.__class__.format_list)
        self.set_formater(tuple, self.__class__.format_tuple)

    def set_formater(self, obj, callback):
        self.types[obj] = callback

    def __call__(self, value, **args):
        for key in args:
            setattr(self, key, args[key])
        formater = self.types[type(value) if type(value) in self.types else object]
        return formater(self, value, self.indent)

    def format_object(self, value, indent):
        return repr(value)

    def format_dict(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + repr(key) + ': ' +
            (self.types[type(value[key]) if type(value[key]) in self.types else object])(self, value[key], indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_list(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + self.lfchar + self.htchar * indent)

    def format_tuple(self, value, indent):
        items = [
            self.lfchar + self.htchar * (indent + 1) + (self.types[type(item) if type(item) in self.types else object])(self, item, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + self.lfchar + self.htchar * indent)

初始化它:

pretty = Formatter()

它可以支持为已定义类型添加格式化程序,您只需要像这个例子一样创建一个函数,并使用set_formater将其绑定到所需类型:

from collections import OrderedDict

def format_ordereddict(self, value, indent):
    items = [
        self.lfchar + self.htchar * (indent + 1) +
        "(" + repr(key) + ', ' + (self.types[
            type(value[key]) if type(value[key]) in self.types else object
        ])(self, value[key], indent + 1) + ")"
        for key in value
    ]
    return 'OrderedDict([%s])' % (','.join(items) +
           self.lfchar + self.htchar * indent)
pretty.set_formater(OrderedDict, format_ordereddict)

由于历史原因,我保留了以前的漂亮打印机,它是一个函数而不是一个类,但是两者都可以以相同的方式使用,类版本只允许更多的功能:

def pretty(value, htchar='\t', lfchar='\n', indent=0):
    nlch = lfchar + htchar * (indent + 1)
    if type(value) is dict:
        items = [
            nlch + repr(key) + ': ' + pretty(value[key], htchar, lfchar, indent + 1)
            for key in value
        ]
        return '{%s}' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is list:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '[%s]' % (','.join(items) + lfchar + htchar * indent)
    elif type(value) is tuple:
        items = [
            nlch + pretty(item, htchar, lfchar, indent + 1)
            for item in value
        ]
        return '(%s)' % (','.join(items) + lfchar + htchar * indent)
    else:
        return repr(value)

使用方法:

>>> a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':pretty,'unicode':u'\xa7',("tuple","key"):"valid"}
>>> a
{'function': <function pretty at 0x7fdf555809b0>, 'tuple': ('a', 'b', 1, 2), 'list': ['a', 'b', 1, 2], 'dict': {'a': 1, 2: 'b'}, 'unicode': u'\xa7', ('tuple', 'key'): 'valid'}
>>> print(pretty(a))
{
    'function': <function pretty at 0x7fdf555809b0>,
    'tuple': (
        'a',
        'b',
        1,
        2
    ),
    'list': [
        'a',
        'b',
        1,
        2
    ],
    'dict': {
        'a': 1,
        2: 'b'
    },
    'unicode': u'\xa7',
    ('tuple', 'key'): 'valid'
}

与其他版本相比:
  • 该解决方案直接查找对象类型,因此您可以漂亮地打印几乎所有内容,不仅限于列表或字典。
  • 没有任何依赖项。
  • 所有内容都放在一个字符串中,因此您可以随心所欲地使用它。
  • 该类和函数已经经过测试,适用于Python 2.7和3.4。
  • 您可以拥有各种类型的对象,这是它们的表示形式,不是它们的内容被放入结果中(因此字符串带有引号,Unicode字符串完全呈现...)。
  • 使用类版本,您可以为想要的每种对象类型添加格式,或更改已定义的对象类型。
  • 键可以是任何有效类型。
  • 缩进和换行符可以更改为任何我们想要的东西。
  • 字典、列表和元组都被漂亮地打印了出来。

5
这绝对应该成为被采纳的解决方案——不依赖JSON是一个很大的优点。 - Josh
如果它能够通过将对象转换为字典并将其键设置为对象类型来实现,那就太棒了。 - Alex Cory
你可以基本上在内部或外部替换format_object方法来实现这一点。 - y.petremann
2
set_formater - 需要两个t,这是一个拼写错误,应该是formatter。 - Nikolay Prokopyev
受@AlexCory的启发,我最终在我的prettyformatter中实现了这个想法,它允许用户轻松地为自定义类添加漂亮的格式,并将它们序列化为列表和/或字典。 - Simply Beautiful Art
@Josh,JSON已经在标准库中了。依赖它会有什么大问题吗? - julaine

22

现代的解决方案是使用rich。安装方法如下:

pip install rich

并用作

from rich import print

d = {
    "Alabama": "Montgomery",
    "Alaska": "Juneau",
    "Arizona": "Phoenix",
    "Arkansas": "Little Rock",
    "California": "Sacramento",
    "Colorado": "Denver",
    "Connecticut": "Hartford",
    "Delaware": "Dover",
    "Florida": "Tallahassee",
    "Georgia": "Atlanta",
    "Hawaii": "Honolulu",
    "Idaho": "Boise",
}
print(d)

输出结果缩进得很整齐:

enter image description here


尝试了几种不同的pprint和json.dumps方法,但都没有成功,但这个方法非常好用! - Christian Wilkie
1
除非我漏掉了什么,否则这只适用于想要打印到控制台的情况,但如果要记录到日志文件中,则不适用,因为它的整个功能是“打印”。如果这个库实际上返回一个可用的值,那就太好了。 - Eitel Dagnin
Rich的文档(https://github.com/Textualize/rich#rich-print)指出它也可以在终端和Jupyter笔记本上使用。@EitelDagnin,您正在寻找一个格式化程序,而不仅仅是像OP所要求的“漂亮打印机”。 - Rich Lysakowski PhD
到目前为止最好的解决方案。 - undefined

18

我还必须传递default参数,像这样:

print(json.dumps(my_dictionary, indent=4, default=str))

如果您想要排序的键,则可以执行以下操作:

print(json.dumps(my_dictionary, sort_keys=True, indent=4, default=str))

为了修复这个类型错误:

TypeError: Object of type 'datetime' is not JSON serializable

这是由于字典中的一些值为日期时间而导致的。


这仅适用于值,而不适用于键。具有元组作为键的字典仍将引发类型错误。 - julaine

14

yapf提供的另一个选项:

from pprint import pformat
from yapf.yapflib.yapf_api import FormatCode

dict_example = {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5], '4': {'1': '1', '2': '2', '3': [1, 2, 3, 4, 5]}}
dict_string = pformat(dict_example)
formatted_code, _ = FormatCode(dict_string)

print(formatted_code)

输出:

{
    '1': '1',
    '2': '2',
    '3': [1, 2, 3, 4, 5],
    '4': {
        '1': '1',
        '2': '2',
        '3': [1, 2, 3, 4, 5]
    }
}

这个解决方案假设 yapf 已经默认安装,但实际上并没有。你可以通过 pip install yapf 来安装它。如果你使用的是 conda,则可以通过 conda install yapf 来安装。 - Medhat
1
当你的字典中有非 JSON 键(例如元组)时,这将非常有用。 - Danilo Gómez
为什么在序列化字典时要使用pformat而不是repr呢?因为yafp实际上是格式化器,它只需要一个字符串。 - Danilo Gómez

8

你可以使用print-dict

from print_dict import pd

dict1 = {
    'key': 'value'
} 

pd(dict1)

输出:

{
    'key': 'value'
}

this Python 代码的输出结果:

{
    'one': 'value-one',
    'two': 'value-two',
    'three': 'value-three',
    'four': {
        '1': '1',
        '2': '2',
        '3': [1, 2, 3, 4, 5],
        '4': {
            'method': <function custom_method at 0x7ff6ecd03e18>,
            'tuple': (1, 2),
            'unicode': '✓',
            'ten': 'value-ten',
            'eleven': 'value-eleven',
            '3': [1, 2, 3, 4]
        }
    },
    'object1': <__main__.Object1 object at 0x7ff6ecc588d0>,
    'object2': <Object2 info>,
    'class': <class '__main__.Object1'>
}

安装:
$ pip install print-dict

披露:我是print-dict的作者。


3
这是最简单的解决方案。非常适用于具有许多嵌套键的大型字典。 - Nairum

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