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

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

6
正如其他人发布的一样,您可以使用递归/深度优先搜索来打印嵌套字典数据,并在其为字典时进行递归调用;否则,打印数据。
def print_json(data):
    if type(data) == dict:
            for k, v in data.items():
                    print k
                    print_json(v)
    else:
            print data

6

pout 可以美化任何你想要的东西,例如(从其他答案中借用data):

data = {'a':2, 'b':{'x':3, 'y':{'t1': 4, 't2':5}}}
pout.vs(data)

将其输出到屏幕,结果如下:

{
    'a': 2,
    'b':
    {
        'y':
        {
            't2': 5,
            't1': 4
        },
        'x': 3
    }
}

或者您可以返回对象的格式化字符串输出:
v = pout.s(data)

它的主要用途是用于调试,因此不会在对象实例或其他任何内容上出现故障,并且处理Unicode输出与您所期望的相同,在Python 2.7和3中均可使用。

披露:我是pout的作者和维护者。


很遗憾,它无法在Windows上运行,如果您能清楚地显示支持的系统将会更好。 - Mandera
@Mandera 如果你能在 https://github.com/Jaymon/pout/issues 提交一个问题,附上详细信息,因为我想不出为什么它在 Windows 上无法工作,我们可以在那里继续对话。 - Jaymon
当然没问题!我错了,以为你不想支持它。 - Mandera

5

prettyformatter

声明:我是这个包的作者。

如果想要和其他格式化工具进行比较,请查看其他格式化工具


格式化

pprint.pprint不同,prettyformatter会更加纵向展开并尝试更好地对齐项目。

json.dumps不同,prettyformatter通常更加紧凑,并在合理的情况下尝试对齐字典值。

from prettyformatter import pprint

batters = [
    {"id": "1001", "type": "Regular"},
    {"id": "1002", "type": "Chocolate"},
    {"id": "1003", "type": "Blueberry"},
    {"id": "1004", "type": "Devil's Food"},
]

toppings = [
    {"id": "5001", "type": None},
    {"id": "5002", "type": "Glazed"},
    {"id": "5005", "type": "Sugar"},
    {"id": "5007", "type": "Powdered Sugar"},
    {"id": "5006", "type": "Chocolate with Sprinkles"},
    {"id": "5003", "type": "Chocolate"},
    {"id": "5004", "type": "Maple"},
]

data = {"id": "0001", "type": "donut", "name": "Cake", "ppu": 0.55, "batters": batters, "topping": toppings}

pprint(data)

输出:

{
    "id"    : "0001",
    "type"  : "donut",
    "name"  : "Cake",
    "ppu"   : 0.55,
    "batters":
        [
            {"id": "1001", "type": "Regular"},
            {"id": "1002", "type": "Chocolate"},
            {"id": "1003", "type": "Blueberry"},
            {"id": "1004", "type": "Devil's Food"},
        ],
    "topping":
        [
            {"id": "5001", "type": None},
            {"id": "5002", "type": "Glazed"},
            {"id": "5005", "type": "Sugar"},
            {"id": "5007", "type": "Powdered Sugar"},
            {"id": "5006", "type": "Chocolate with Sprinkles"},
            {"id": "5003", "type": "Chocolate"},
            {"id": "5004", "type": "Maple"},
        ],
}

特性

完整文档请查看这里

JSON

prettyformatter 不同于 pprint.pprint,支持通过 json=True 参数进行 JSON 转换。这个过程包括将 None 转换为 nullTrue 转换为 trueFalse 转换为 false,并正确使用引号。

prettyformatter 不同于 json.dumps,支持更多数据类型的 JSON 强制转换。这个过程包括将任何 dataclassmapping 转换为 dict,并将任何 iterable 转换为 list

from dataclasses import dataclass

from prettyformatter import PrettyDataclass, pprint


@dataclass(unsafe_hash=True)
class Point(PrettyDataclass):
    x: int
    y: int


pprint((Point(1, 2), Point(3, 4)), json=True)

输出:

[{"x": 1, "y": 2}, {"x": 3, "y": 4}]

自定义

prettyformatter不同于pprint.pprintjson.dumps,它支持通过添加类型来轻松进行自定义。

prettyformatter.PrettyClass子类实现__pargs__和/或__pkwargs__方法可以轻松地自定义以"cls_name(*args, **kwargs)"形式表示的类。

from prettyformatter import PrettyClass


class Dog(PrettyClass):

    def __init__(self, name, **kwargs):
        self.name = name

    def __pkwargs__(self):
        return {"name": self.name}


print(Dog("Fido"))
"""
Dog(name="Fido")
"""

print(Dog("Fido"), json=True)
"""
{"name": "Fido"}
"""

实现 __pformat__ 方法可以更具体地实现 pformat 函数。

同时,实现 @prettyformatter.register 函数也允许以与实现 __pformat__ 相同的方式自定义已存在的类。

import numpy as np
from prettyformatter import pprint, register

@register(np.ndarray)
def pformat_ndarray(obj, specifier, depth, indent, shorten, json):
    if json:
        return pformat(obj.tolist(), specifier, depth, indent, shorten, json)
    with np.printoptions(formatter=dict(all=lambda x: format(x, specifier))):
        return repr(obj).replace("\n", "\n" + " " * depth)

pprint(dict.fromkeys("ABC", np.arange(9).reshape(3, 3)))

输出:

{
    "A":
        array([[0, 1, 2],
               [3, 4, 5],
               [6, 7, 8]]),
    "B":
        array([[0, 1, 2],
               [3, 4, 5],
               [6, 7, 8]]),
    "C":
        array([[0, 1, 2],
               [3, 4, 5],
               [6, 7, 8]]),
}

4

我使用了sth的答案,稍作修改以适应我需要的嵌套字典和列表:

def pretty(d, indent=0):
    if isinstance(d, dict):
        for key, value in d.iteritems():
            print '\t' * indent + str(key)
            if isinstance(value, dict) or isinstance(value, list):
                pretty(value, indent+1)
            else:
                print '\t' * (indent+1) + str(value)
    elif isinstance(d, list):
        for item in d:
            if isinstance(item, dict) or isinstance(item, list):
                pretty(item, indent+1)
            else:
                print '\t' * (indent+1) + str(item)
    else:
        pass

然后我会得到如下输出:

>>> 
xs:schema
    @xmlns:xs
        http://www.w3.org/2001/XMLSchema
    xs:redefine
        @schemaLocation
            base.xsd
        xs:complexType
            @name
                Extension
            xs:complexContent
                xs:restriction
                    @base
                        Extension
                    xs:sequence
                        xs:element
                            @name
                                Policy
                            @minOccurs
                                1
                            xs:complexType
                                xs:sequence
                                    xs:element
                                            ...

3
我运用了你们所教授的知识,结合装饰器的强大功能重载了经典的print函数。只需根据需要更改缩进即可。我在github中添加了这个代码片段,以便您收藏它。
def print_decorator(func):
    """
    Overload Print function to pretty print Dictionaries 
    """
    def wrapped_func(*args,**kwargs):
        if isinstance(*args, dict):
            return func(json.dumps(*args, sort_keys=True, indent=2, default=str))
        else:
            return func(*args,**kwargs)
    return wrapped_func
print = print_decorator(print)

现在就像往常一样使用print即可。


2
注意:此代码仅适用于Python 3,因为在Python 2中,“print”是一个关键字,不能被赋值。 - TheEagle

2

在采用sth的答案并进行了一些小的但非常有用的修改后,我回到了这个问题。该函数会打印出JSON树中所有的以及该树中叶节点的大小

def print_JSON_tree(d, indent=0):
    for key, value in d.iteritems():
        print '    ' * indent + unicode(key),
        if isinstance(value, dict):
            print; print_JSON_tree(value, indent+1)
        else:
            print ":", str(type(d[key])).split("'")[1], "-", str(len(unicode(d[key])))

当你有一个大的JSON对象,想要找出其中重点内容时,这个工具非常好用。示例

>>> print_JSON_tree(JSON_object)
key1
    value1 : int - 5
    value2 : str - 16
    key2
       value1 : str - 34
       value2 : list - 5623456

这说明你关心的大部分数据可能在JSON_object['key1']['key2']['value2']中,因为该值作为字符串格式化后的长度非常大。

2
我用Python编写了一个简单的代码,用于打印JSON对象的一般结构。
def getstructure(data, tab = 0):
    if type(data) is dict:
        print ' '*tab + '{' 
        for key in data:
            print ' '*tab + '  ' + key + ':'
            getstructure(data[key], tab+4)
        print ' '*tab + '}'         
    elif type(data) is list and len(data) > 0:
        print ' '*tab + '['
        getstructure(data[0], tab+4)
        print ' '*tab + '  ...'
        print ' '*tab + ']'

以下是数据的结果。
a = {'list':['a','b',1,2],'dict':{'a':1,2:'b'},'tuple':('a','b',1,2),'function':'p','unicode':u'\xa7',("tuple","key"):"valid"}
getstructure(a)

非常紧凑,外观如下:
{
  function:
  tuple:
  list:
    [
      ...
    ]
  dict:
    {
      a:
      2:
    }
  unicode:
  ('tuple', 'key'):
}

2
最简单的方法是安装IPython并使用以下代码:
from IPython.lib.pretty import pretty


class MyClass:
    __repr__(self):
       return pretty(data)  # replace data with what makes sense

在你的情况下
print(pretty(mydict))

2
我尝试了以下方法并得到了我想要的结果。 方法1: 步骤1:在cmd中输入以下命令安装print_dict
pip install print_dict

步骤二:导入print_dict as
from print_dict import pd

第三步:使用pd进行打印。
pd(your_dictionary_name)

示例输出:

{
    'Name': 'Arham Rumi',
    'Age': 21,
    'Movies': ['adas', 'adfas', 'fgfg', 'gfgf', 'vbxbv'],
    'Songs': ['sdfsd', 'dfdgfddf', 'dsdfd', 'sddfsd', 'sdfdsdf']
}

方法二:我们也可以使用for循环和items方法来打印字典。

for key, Value in your_dictionary_name.items():
    print(f"{key} : {Value}")

这里的方法2是我在这个SO帖子中看到的最好的之一,因为它仅使用核心Python 3,没有JSON导入。通过一点动态格式化,它可以打印出漂亮的表格嵌套输出。 - Rich Lysakowski PhD

1

我觉得这很不错 ;)

def pretty(d, indent=0):
    for key, value in d.iteritems():
        if isinstance(value, dict):
            print '\t' * indent + (("%30s: {\n") % str(key).upper())
            pretty(value, indent+1)
            print '\t' * indent + ' ' * 32 + ('} # end of %s #\n' % str(key).upper())
        elif isinstance(value, list):
            for val in value:
                print '\t' * indent + (("%30s: [\n") % str(key).upper())
                pretty(val, indent+1)
                print '\t' * indent + ' ' * 32 + ('] # end of %s #\n' % str(key).upper())
        else:
            print '\t' * indent + (("%30s: %s") % (str(key).upper(),str(value)))

2
-1:无法处理不是dict实例的list值,即pretty({'key': [1, 2, 3]}, indent=4)==> AttributeError:'int' object has no attribute 'iteritems'。而且我也不喜欢它将键名大写化。 - martineau
你的解决方案考虑到根字典中不能有列表内嵌套字典。同时,它也考虑到我们不想美化打印列表或元组。最后,不要将键大写,否则 {'a':0,'A':1} 的结果将不正确。 - y.petremann

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