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

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个回答

1
晚了一些,但这个答案不需要任何额外的库。类似于STH的答案,但格式更加健壮,并返回一个完整的字符串,然后可以打印出来。
def pretty_print_dict(
        input_dictionary,
        indent=1,
        depth=0
):
    # Bool flag to add comma's after first item in dict.
    needs_comma = False
    # String for any dict will start with a '{'
    return_string = '\t' * depth + '{\n'
    # Iterate over keys and values, building the full string out.
    for key, value in input_dictionary.items():
        # Start with key. If key follows a previous item, add comma.
        if needs_comma:
            return_string = return_string + ',\n' + '\t' * (depth + 1) + str(key) + ': '
        else:
            return_string = return_string + '\t' * (depth + 1) + str(key) + ': '
        # If the value is a dict, recursively call function.
        if isinstance(value, dict):
            return_string = return_string + '\n' + pretty_print_dict(value, depth=depth+2)
        else:
            return_string = return_string + '\t' * indent + str(value)
        # After first line, flip bool to True to make sure commas make it.
        needs_comma = True
    # Complete the dict with a '}'
    return_string = return_string + '\n' + '\t' * depth + '}'
    # Return dict string.
    return return_string

让我们看一下它如何处理像test_dict={1: 2, 3: {4: {5: 6}, 7: 8}, 9: 10}这样的字典。
该字符串看起来像:'{\n\t1: \t2,\n\t3: \n\t\t{\n\t\t\t4: \n\t\t\t\t{\n\t\t\t\t\t5: \t6\n\t\t\t\t},\n\t\t\t7: \t8\n\t\t},\n\t9: \t10\n}'
打印该字符串会产生以下结果:
{
    1:  2,
    3: 
        {
            4: 
                {
                    5:  6
                },
            7:  8
        },
    9:  10
}

1
This class prints out a complex nested dictionary with sub dictionaries and sub lists.  
##
## Recursive class to parse and print complex nested dictionary
##

class NestedDictionary(object):
    def __init__(self,value):
        self.value=value

    def print(self,depth):
        spacer="--------------------"
        if type(self.value)==type(dict()):
            for kk, vv in self.value.items():
                if (type(vv)==type(dict())):
                    print(spacer[:depth],kk)
                    vvv=(NestedDictionary(vv))
                    depth=depth+3
                    vvv.print(depth)
                    depth=depth-3
                else:
                    if (type(vv)==type(list())):
                        for i in vv:
                            vvv=(NestedDictionary(i))
                            depth=depth+3
                            vvv.print(depth)
                            depth=depth-3
                    else:
                        print(spacer[:depth],kk,vv) 

##
## Instatiate and execute - this prints complex nested dictionaries
## with sub dictionaries and sub lists
## 'something' is a complex nested dictionary

MyNest=NestedDictionary(weather_com_result)
MyNest.print(0)

1

这里有许多不错的实现,让我也想加入其中:)。我在CircuitPython和MicroPython中用它进行调试,因为json.dumps不允许使用indent参数,也不支持pprint

它使用self来实现,所以可以插入到类中,在每个数据上显示数据类型,这对于调试非常有用。不依赖于任何外部模块。

def pretty_print_dict(self, d, indent=0):
    INDENT = 2
    if isinstance(d, dict):
        print(' ' * indent + '{')
        for key, value in d.items():
            print(f'{" " * (indent + INDENT)}{key}:')
            self.pretty_print_dict(value, indent + 2 * INDENT)
        print(' ' * indent + '}')
    elif isinstance(d, list):
        print(' ' * indent + '[')
        for item in d:
            self.pretty_print_dict(item, indent + INDENT)
        print(' ' * indent + ']')
    elif isinstance(d, str):
        print(' ' * indent + '<s>' + d + '</s>')
    elif isinstance(d, int):
        print(' ' * indent + '<i>' + str(d) + '</i>')
    elif isinstance(d, bool):
        print(' ' * indent + '<b>' + str(d) + '</b>')
    elif isinstance(d, float):
        print(' ' * indent + '<f>' + str(d) + '</f>')
    else:
        print(' ' * indent + '<?>' + str(d) + '</?>')

用法:self.pretty_print_dict(my_dict)

0
这里有一段代码,可以打印任何嵌套字典的内容,并在过程中跟踪“父”字典。
dicList = list()

def prettierPrint(dic, dicList):
count = 0
for key, value in dic.iteritems():
    count+=1
    if str(value) == 'OrderedDict()':
        value = None
    if not isinstance(value, dict):
        print str(key) + ": " + str(value)
        print str(key) + ' was found in the following path:',
        print dicList
        print '\n'
    elif isinstance(value, dict):
        dicList.append(key)
        prettierPrint(value, dicList)
    if dicList:
         if count == len(dic):
             dicList.pop()
             count = 0

prettierPrint(dicExample, dicList)

这是一个很好的起点,用于根据不同格式打印,比如OP中指定的格式。你真正需要做的就是在Print块周围进行操作。请注意,它会查看值是否为“OrderedDict()”。根据您是否使用容器数据类型集合中的内容,您应该制作这些故障保护,以便elif块不会将其视为附加字典,因为其名称相同。目前为止,例如字典如下:

example_dict = {'key1': 'value1',
            'key2': 'value2',
            'key3': {'key3a': 'value3a'},
            'key4': {'key4a': {'key4aa': 'value4aa',
                               'key4ab': 'value4ab',
                               'key4ac': 'value4ac'},
                     'key4b': 'value4b'}

将会打印
key3a: value3a
key3a was found in the following path: ['key3']

key2: value2
key2 was found in the following path: []

key1: value1
key1 was found in the following path: []

key4ab: value4ab
key4ab was found in the following path: ['key4', 'key4a']

key4ac: value4ac
key4ac was found in the following path: ['key4', 'key4a']

key4aa: value4aa
key4aa was found in the following path: ['key4', 'key4a']

key4b: value4b
key4b was found in the following path: ['key4']

修改代码以适应问题的格式

lastDict = list()
dicList = list()
def prettierPrint(dic, dicList):
    global lastDict
    count = 0
    for key, value in dic.iteritems():
        count+=1
        if str(value) == 'OrderedDict()':
            value = None
        if not isinstance(value, dict):
            if lastDict == dicList:
                sameParents = True
            else:
                sameParents = False

            if dicList and sameParents is not True:
                spacing = ' ' * len(str(dicList))
                print dicList
                print spacing,
                print str(value)

            if dicList and sameParents is True:
                print spacing,
                print str(value)
            lastDict = list(dicList)

        elif isinstance(value, dict):
            dicList.append(key)
            prettierPrint(value, dicList)

        if dicList:
             if count == len(dic):
                 dicList.pop()
                 count = 0

使用相同的示例代码,将打印以下内容:
['key3']
         value3a
['key4', 'key4a']
                  value4ab
                  value4ac
                  value4aa
['key4']
         value4b

这并不是 OP 请求的完全内容。区别在于,父^n 仍然会被打印出来,而不是缺失并替换为空格。要达到 OP 的格式,您需要执行以下操作:迭代比较 dicListlastDict。您可以通过创建一个新字典并将 dicList 的内容复制到其中来实现此目的,检查复制的字典中的 i 是否与 lastDict 中的 i 相同,如果相同,则使用字符串乘法器在该 i 位置写入空格。


0

我自己也是一个相对初学者,但过去几周一直在使用嵌套字典进行编程工作,下面是我提出的解决方案。

你可以尝试使用堆栈。将根字典的键制作成一个列表的列表:

stack = [ root.keys() ]     # Result: [ [root keys] ]

从后往前的顺序,查找字典中的每个键,看它的值是否也是一个字典。如果不是,则打印该键并将其删除。但是,如果该键的值是一个字典,则打印该键,然后将该值的键附加到堆栈的末尾,并以相同的方式开始处理该列表,对于每个新的键列表重复递归。

如果每个列表中第二个键的值都是一个字典,那么在几轮之后,您会得到以下结果:

[['key 1','key 2'],['key 2.1','key 2.2'],['key 2.2.1','key 2.2.2'],[`etc.`]]

这种方法的优点是缩进只需使用堆栈长度的\t倍:

indent = "\t" * len(stack)

缺点是为了检查每个键,您需要通过哈希到相关的子字典,不过这可以轻松处理,只需使用列表推导和简单的for循环:

path = [li[-1] for li in stack]
# The last key of every list of keys in the stack

sub = root
for p in path:
    sub = sub[p]


if type(sub) == dict:
    stack.append(sub.keys()) # And so on

请注意,这种方法需要您清理尾随的空列表,并删除任何列表中的最后一个键,后面跟着一个空列表(当然可能会创建另一个空列表,依此类推)。

还有其他实现此方法的方式,但希望这可以给您提供一个基本的思路。

编辑:如果您不想经历所有这些,pprint 模块可以以良好的格式打印嵌套字典。


0
这是我根据sth的评论编写的一个函数。它与json.dumps具有相同的缩进功能,但我使用制表符而不是空格进行缩进。在Python 3.2+中,您可以直接将缩进指定为'\t',但在2.7中不行。
def pretty_dict(d):
    def pretty(d, indent):
        for i, (key, value) in enumerate(d.iteritems()):
            if isinstance(value, dict):
                print '{0}"{1}": {{'.format( '\t' * indent, str(key))
                pretty(value, indent+1)
                if i == len(d)-1:
                    print '{0}}}'.format( '\t' * indent)
                else:
                    print '{0}}},'.format( '\t' * indent)
            else:
                if i == len(d)-1:
                    print '{0}"{1}": "{2}"'.format( '\t' * indent, str(key), value)
                else:
                    print '{0}"{1}": "{2}",'.format( '\t' * indent, str(key), value)
    print '{'
    pretty(d,indent=1)
    print '}'

例:

>>> dict_var = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
>>> pretty_dict(dict_var)
{
    "a": "2",
    "b": {
        "y": {
            "t2": "5",
            "t1": "4"
        },
        "x": "3"
    }
}

我无法使用嵌套字典使其正常工作,因为它会导致UnicodeEncodeError错误,而且所有的项和键都被转换为字符串,如果我们使用包含列表和字典的数字或元组呢?最后,您的解决方案考虑到我们要漂亮打印的对象必须是一个字典。 - y.petremann
我并不是在尝试为Python字典编写一个通用的打印函数。排名靠前的评论已经展示了如何漂亮地打印一个字典。我的贡献是编写了一个替代json.dumps的方案,在Python 2.7中使用'\t'进行缩进,而不是制表符。 - Al Conrad
我同意你关于编写json.dumps的替代方案的看法,对我来说,与json.dumps相同的问题也存在。此外,您可以使用简单的正则表达式来更改缩进类型,使您的代码更简洁。 - y.petremann

0

虽然这个链接可能回答了问题,但最好在这里包含答案的关键部分,并提供链接作为参考。仅有链接的答案如果链接页面发生变化,就可能失效。- 来自审查 - TheTridentGuy supports Ukraine

0

来自此链接

def prnDict(aDict, br='\n', html=0,
            keyAlign='l',   sortKey=0,
            keyPrefix='',   keySuffix='',
            valuePrefix='', valueSuffix='',
            leftMargin=0,   indent=1 ):
    '''
return a string representive of aDict in the following format:
    {
     key1: value1,
     key2: value2,
     ...
     }

Spaces will be added to the keys to make them have same width.

sortKey: set to 1 if want keys sorted;
keyAlign: either 'l' or 'r', for left, right align, respectively.
keyPrefix, keySuffix, valuePrefix, valueSuffix: The prefix and
   suffix to wrap the keys or values. Good for formatting them
   for html document(for example, keyPrefix='<b>', keySuffix='</b>'). 
   Note: The keys will be padded with spaces to have them
         equally-wide. The pre- and suffix will be added OUTSIDE
         the entire width.
html: if set to 1, all spaces will be replaced with '&nbsp;', and
      the entire output will be wrapped with '<code>' and '</code>'.
br: determine the carriage return. If html, it is suggested to set
    br to '<br>'. If you want the html source code eazy to read,
    set br to '<br>\n'

version: 04b52
author : Runsun Pan
require: odict() # an ordered dict, if you want the keys sorted.
         Dave Benjamin 
         http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/161403
    '''

    if aDict:

        #------------------------------ sort key
        if sortKey:
            dic = aDict.copy()
            keys = dic.keys()
            keys.sort()
            aDict = odict()
            for k in keys:
                aDict[k] = dic[k]

        #------------------- wrap keys with ' ' (quotes) if str
        tmp = ['{']
        ks = [type(x)==str and "'%s'"%x or x for x in aDict.keys()]

        #------------------- wrap values with ' ' (quotes) if str
        vs = [type(x)==str and "'%s'"%x or x for x in aDict.values()] 

        maxKeyLen = max([len(str(x)) for x in ks])

        for i in range(len(ks)):

            #-------------------------- Adjust key width
            k = {1            : str(ks[i]).ljust(maxKeyLen),
                 keyAlign=='r': str(ks[i]).rjust(maxKeyLen) }[1]

            v = vs[i]        
            tmp.append(' '* indent+ '%s%s%s:%s%s%s,' %(
                        keyPrefix, k, keySuffix,
                        valuePrefix,v,valueSuffix))

        tmp[-1] = tmp[-1][:-1] # remove the ',' in the last item
        tmp.append('}')

        if leftMargin:
          tmp = [ ' '*leftMargin + x for x in tmp ]

        if html:
            return '<code>%s</code>' %br.join(tmp).replace(' ','&nbsp;')
        else:
            return br.join(tmp)     
    else:
        return '{}'

'''
Example:

>>> a={'C': 2, 'B': 1, 'E': 4, (3, 5): 0}

>>> print prnDict(a)
{
 'C'   :2,
 'B'   :1,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, sortKey=1)
{
 'B'   :1,
 'C'   :2,
 'E'   :4,
 (3, 5):0
}

>>> print prnDict(a, keyPrefix="<b>", keySuffix="</b>")
{
 <b>'C'   </b>:2,
 <b>'B'   </b>:1,
 <b>'E'   </b>:4,
 <b>(3, 5)</b>:0
}

>>> print prnDict(a, html=1)
<code>{
&nbsp;'C'&nbsp;&nbsp;&nbsp;:2,
&nbsp;'B'&nbsp;&nbsp;&nbsp;:1,
&nbsp;'E'&nbsp;&nbsp;&nbsp;:4,
&nbsp;(3,&nbsp;5):0
}</code>

>>> b={'car': [6, 6, 12], 'about': [15, 9, 6], 'bookKeeper': [9, 9, 15]}

>>> print prnDict(b, sortKey=1)
{
 'about'     :[15, 9, 6],
 'bookKeeper':[9, 9, 15],
 'car'       :[6, 6, 12]
}

>>> print prnDict(b, keyAlign="r")
{
        'car':[6, 6, 12],
      'about':[15, 9, 6],
 'bookKeeper':[9, 9, 15]
}
'''

0
请使用这个函数:
def pretty_dict(d, n=1):
    for k in d:
        print(" "*n + k)
        try:
            pretty_dict(d[k], n=n+4)
        except TypeError:
            continue

像这样调用:

pretty_dict(mydict)

1
如果值是字符串,这个方法就不起作用了。它会将字符串的每个字符打印在新行上,但键似乎还可以正常工作。 - Anthony

0

在编写需要将字典写入 .txt 文件的类时,这是我想出来的:

@staticmethod
def _pretty_write_dict(dictionary):

    def _nested(obj, level=1):
        indentation_values = "\t" * level
        indentation_braces = "\t" * (level - 1)
        if isinstance(obj, dict):
            return "{\n%(body)s%(indent_braces)s}" % {
                "body": "".join("%(indent_values)s\'%(key)s\': %(value)s,\n" % {
                    "key": str(key),
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for key, value in obj.items()),
                "indent_braces": indentation_braces
            }
        if isinstance(obj, list):
            return "[\n%(body)s\n%(indent_braces)s]" % {
                "body": "".join("%(indent_values)s%(value)s,\n" % {
                    "value": _nested(value, level + 1),
                    "indent_values": indentation_values
                } for value in obj),
                "indent_braces": indentation_braces
            }
        else:
            return "\'%(value)s\'" % {"value": str(obj)}

    dict_text = _nested(dictionary)
    return dict_text

现在,如果我们有这样一个字典:

some_dict = {'default': {'ENGINE': [1, 2, 3, {'some_key': {'some_other_key': 'some_value'}}], 'NAME': 'some_db_name', 'PORT': '', 'HOST': 'localhost', 'USER': 'some_user_name', 'PASSWORD': 'some_password', 'OPTIONS': {'init_command': 'SET foreign_key_checks = 0;'}}}

我们做的是:

print(_pretty_write_dict(some_dict))

我们得到:
{
    'default': {
        'ENGINE': [
            '1',
            '2',
            '3',
            {
                'some_key': {
                    'some_other_key': 'some_value',
                },
            },
        ],
        'NAME': 'some_db_name',
        'OPTIONS': {
            'init_command': 'SET foreign_key_checks = 0;',
        },
        'HOST': 'localhost',
        'USER': 'some_user_name',
        'PASSWORD': 'some_password',
        'PORT': '',
    },
}

您可以将其简化为将对象类型与包含类型+相应字符串的另一个字典进行匹配。 - Jay-Pi

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