如何优雅地格式化字典字符串输出

72

我想知道是否有一种简单的方法来格式化像这样的字典输出字符串:

{
  'planet' : {
    'name' : 'Earth',
    'has' : {
      'plants' : 'yes',
      'animals' : 'yes',
      'cryptonite' : 'no'
    }
  }
}

在这种情况下,简单的 str(dict) 只会给你一个相当难以阅读的结果...

{'planet' : {'has': {'plants': 'yes', 'animals': 'yes', 'cryptonite': 'no'}, 'name': 'Earth'}}

尽管我对Python有一定了解,但为了解决这个问题,我需要编写很多代码,包括许多特殊情况和字符串替换调用,而这个问题本身并不像是一个需要1000行代码的问题。

请建议一种最简单的方法来按照此格式格式化任何字典。

4个回答

118

根据你对输出的处理方式,其中一种选择是使用JSON进行显示。

import json
x = {'planet' : {'has': {'plants': 'yes', 'animals': 'yes', 'cryptonite': 'no'}, 'name': 'Earth'}}

print json.dumps(x, indent=2)

输出:

{
  "planet": {
    "has": {
      "plants": "yes", 
      "animals": "yes", 
      "cryptonite": "no"
    }, 
    "name": "Earth"
  }
}

这种方法的警告是,一些内容无法被JSON序列化。如果字典中包含不可序列化的项(如类或函数),则需要编写额外的代码。


我想为你的回答点赞不止一次。太棒了!谢谢,伙计! - erikbstack
4
如果其中一个值是一个日期对象,那么它将无法正常工作。 - archmage
None对象怎么样?它将被转换为null。您需要手动将这些null对象转换为None - Ryan Chou
我认为这不应该是一个可接受的答案,因为虽然json.dumps序列化了字典,但并非所有的Python对象都是JSON可序列化的,而且生成的字符串缺乏适当的格式(截至本文撰写时)。 - Michael Schmidt

43

使用pprint

import pprint

x  = {
  'planet' : {
    'name' : 'Earth',
    'has' : {
      'plants' : 'yes',
      'animals' : 'yes',
      'cryptonite' : 'no'
    }
  }
}
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(x)

这将输出

{   'planet': {   'has': {   'animals': 'yes',
                             'cryptonite': 'no',
                             'plants': 'yes'},
                  'name': 'Earth'}}

尝试使用pprint格式化,并且你就可以得到所需的结果。


1
好的,它只有深度、宽度和缩进作为参数。我看不到让行星换行的方法。当然,PP结果更易读,但换行+适当的空格对我的情况是必要的。但感谢您向我展示这个很棒的包。在其他情况下非常方便。 - erikbstack

7
def format(d, tab=0):
    s = ['{\n']
    for k,v in d.items():
        if isinstance(v, dict):
            v = format(v, tab+1)
        else:
            v = repr(v)

        s.append('%s%r: %s,\n' % ('  '*tab, k, v))
    s.append('%s}' % ('  '*tab))
    return ''.join(s)

print format({'has': {'plants': 'yes', 'animals': 'yes', 'cryptonite': 'no'}, 'name': 'Earth'}})

输出:

{
'planet': {
  'has': {
    'plants': 'yes',
    'animals': 'yes',
    'cryptonite': 'no',
    },
  'name': 'Earth',
  },
}

请注意,我在这里假设所有的键都是字符串,或者至少是漂亮的对象。

1
正如预期的那样工作。不管怎样,我希望有人可以找到一个包罗万象的解决方案。如果周末后没有其他更好的选择,我会使用你的解决方案。谢谢你的帮助! - erikbstack
在包含自身的字典中会陷入无限递归。 - Tigran Saluev

0

这是我用来完成所需功能的一些临时代码,想分享一下:

v = {'has': {1: [213124,1243215,143541,531322], 'plants': 'yes', 'animals': 'yes', 'cryptonite': 'no'}, 'name': 'Earth'}
print(pretty(v))

输出:

{
    'has': {
        1: [
            213124,
            1243215,
            143541,
            531322
        ],
        'plants{}': 'yes',
        'animals': 'yes',
        'cryptonite': 'no'
    },
    'name': 'Earth'
}

不好看的代码:

def pretty(obj, starters='{[', enders='}]', items=','):
    _all = starters+enders+items
    tabs, i, s, inside_quotes = 0, 0, str(obj), ['"', False]
    def step_char(c):
        nonlocal tabs, i, inside_quotes
        tabs += (c in starters) - (c in enders)
        _c = (not (s[max(i-1, 0)] in _all and c == ' ')) * (('\n'+(tabs*'\t')*(c in set(_all)))*(c in set(enders))*(not inside_quotes[1]) + c + ('\n'+tabs*'\t')*(c in set(_all))*(not sum([s[i:].startswith(e+ii) for e in enders for ii in items]))*(not inside_quotes[1]))
        inside_quotes = in_quotes(i, s, inside_quotes)
        i += 1
        return _c
    return ''.join(list(map(step_char, s)))


def in_quotes(index, string, last):
    in_string = last
    for quote in ['"""', "'''", '"', "'"]:
        if string[index].startswith(quote):
            if in_string[1] and in_string[0] == quote:
                in_string[1] = False
                break
            elif not in_string[1]:
                in_string = [quote, True]
                break
    return in_string

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