【问题标题】:Any way to properly pretty-print OrderedDict?有什么方法可以正确打印 OrderedDict?
【发布时间】:2011-05-17 02:37:40
【问题描述】:

我喜欢 Python 中的 pprint 模块。我经常使用它进行测试和调试。我经常使用宽度选项来确保输出完全适合我的终端窗口。

在他们在 Python 2.7 中添加新的 ordered dictionary type 之前它一直运行良好(我非常喜欢的另一个很酷的功能)。如果我尝试漂亮地打印有序字典,它不会很好地显示。不是将每个键值对放在自己的行上,而是将整个内容显示在一行很长的行中,该行包含很多次并且难以阅读:

>>> 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 方法,但我想知道这里是否有人已经知道解决方案。

更新:我为此提交了错误报告。你可以在http://bugs.python.org/issue10592看到它。

【问题讨论】:

  • 建议在bugs.python.org/issue7434添加关于有序字典的评论
  • 此问题现已在较新版本的 Python 3 中得到修复。pprint 将更好地处理 OrderedDict 对象。
  • 似乎很多人将这个问题误解为关于 dict 对象(在较新版本的 Python 中保留插入顺序),而不是关于 OrderedDict 对象。我已经修改了标题以使其更清晰。
  • 如果您不希望 pprintdict 对象中的键顺序进行排序,请参阅此问题:stackoverflow.com/q/25683088/247696
  • 我想要的是字符串,而不是打印。 pprint 搞砸了 f 弦,所以对我来说没用。我直接要字符串。

标签: python python-2.7 pretty-print ordereddictionary pprint


【解决方案1】:

从 Python 3.7 开始,Python 保证字典中的键将保留其插入顺序。 (尽管它们的行为仍然与 OrderedDict 对象不完全相同,因为即使键的顺序不同,两个字典 ab 也可以被视为相等 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
}

【讨论】:

  • 如果顺序不重要的替代方案:pprint.pprint(json.loads(json.dumps(data)))
  • @scottmrogowski 为什么不简单地pprint.pprint(dict(data))
  • 如果您不关心键的顺序,pprint.pprint(dict(data)) 效果很好。就个人而言,我希望OrderedDict__repr__ 会产生这样的输出,但保留键的顺序。
  • @Alfe 如果字典嵌套了 OrderedDicts,它们将不会很好地显示
  • @Alfe:因为那样输出没有排序。首先使用 OrderedDict 而不是 dict 的原因是因为顺序很重要。
【解决方案2】:

如果您的 OrderedDict 的顺序是 alpha 排序,则以下内容将起作用,因为 pprint 将在打印之前对 dict 进行排序。

>>> 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 是按字母顺序排序的。

【讨论】:

  • 由于 OrderedDicts 是按插入顺序排序的,因此这可能适用于一小部分用途。无论如何,将 OD 转换为 dict 应该可以避免将所有内容放在一行中的问题。
  • 我想要的是字符串,而不是打印。 pprint 搞砸了 f 弦,所以对我来说没用。我直接要字符串。
【解决方案3】:

这是通过在内部覆盖和使用 stock pprint() 函数来工作的另一个答案。与我的earlier one 不同,它在另一个容器(例如list)内处理OrderedDict,并且还应该能够处理给定的任何可选关键字参数——但是它没有相同的程度控制对方提供的输出。

它通过将 stock 函数的输出重定向到一个临时缓冲区来进行操作,然后在将其发送到输出流之前对其进行自动换行。虽然产生的最终输出不是特别漂亮,但它很不错,并且可能“足够好”以用作解决方法。

更新 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)])]

【讨论】:

  • 我试过了,效果很好。正如你所说,它不是最漂亮的,但它是我迄今为止看到的最好的解决方案。
  • 我想要的是字符串,而不是打印。 pprint 搞砸了 f 弦,所以对我来说没用。我直接要字符串。
  • @Charlie:那可能是什么字符串?
  • @martineau pprint 显示的是一个不错的候选者。
  • @Charlie:我至少可以想到两种方法来使用我的答案中的代码(以及其他一些)获取该字符串 - 一种是 pprint() 接受 @ 987654340@ 关键字参数。第三种方法需要对其进行相对微不足道的更改。
【解决方案4】:

打印一个有序的字典,例如

from collections import OrderedDict

d=OrderedDict([
    ('a', OrderedDict([
        ('a1',1),
        ('a2','sss')
    ])),
    ('b', OrderedDict([
        ('b1', OrderedDict([
            ('bb1',1),
            ('bb2',4.5)])),
        ('b2',4.5)
    ])),
])

我愿意

def dict_or_OrdDict_to_formatted_str(OD, mode='dict', s="", indent=' '*4, level=0):
    def is_number(s):
        try:
            float(s)
            return True
        except ValueError:
            return False
    def fstr(s):
        return s if is_number(s) else '"%s"'%s
    if mode != 'dict':
        kv_tpl = '("%s", %s)'
        ST = 'OrderedDict([\n'; END = '])'
    else:
        kv_tpl = '"%s": %s'
        ST = '{\n'; END = '}'
    for i,k in enumerate(OD.keys()):
        if type(OD[k]) in [dict, OrderedDict]:
            level += 1
            s += (level-1)*indent+kv_tpl%(k,ST+dict_or_OrdDict_to_formatted_str(OD[k], mode=mode, indent=indent, level=level)+(level-1)*indent+END)
            level -= 1
        else:
            s += level*indent+kv_tpl%(k,fstr(OD[k]))
        if i!=len(OD)-1:
            s += ","
        s += "\n"
    return s

print dict_or_OrdDict_to_formatted_str(d)

产量

"a": {
    "a1": 1,
    "a2": "sss"
},
"b": {
    "b1": {
        "bb1": 1,
        "bb2": 4.5
    },
    "b2": 4.5
}

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

产生

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))

【讨论】:

    【解决方案5】:

    这是一种破解 pprint 实现的方法。 pprint 在打印之前对键进行排序,所以为了保持顺序,我们只需按照我们想要的方式对键进行排序。

    请注意,这会影响items() 函数。 因此,您可能希望在执行 pprint 后保留和恢复被覆盖的函数。

    from collections import OrderedDict
    import pprint
    
    class ItemKey(object):
      def __init__(self, name, position):
        self.name = name
        self.position = position
      def __cmp__(self, b):
        assert isinstance(b, ItemKey)
        return cmp(self.position, b.position)
      def __repr__(self):
        return repr(self.name)
    
    OrderedDict.items = lambda self: [
        (ItemKey(name, i), value)
        for i, (name, value) in enumerate(self.iteritems())]
    OrderedDict.__repr__ = dict.__repr__
    
    a = OrderedDict()
    a[4] = '4'
    a[1] = '1'
    a[2] = '2'
    print pprint.pformat(a) # {4: '4', 1: '1', 2: '2'}
    

    【讨论】:

    • 很好,但最好是子类型然后覆盖函数。
    【解决方案6】:

    这是我漂亮打印 OrderedDict 的方法

    from collections import OrderedDict
    import json
    d = OrderedDict()
    d['duck'] = 'alive'
    d['parrot'] = 'dead'
    d['penguin'] = 'exploded'
    d['Falcon'] = 'discharged'
    print(d)
    print(json.dumps(d,indent=4))
    
    OutPut:
    
    OrderedDict([('duck', 'alive'), ('parrot', 'dead'), ('penguin', 'exploded'), ('Falcon', 'discharged')])
    
    {
        "duck": "alive",
        "parrot": "dead",
        "penguin": "exploded",
        "Falcon": "discharged"
    }
    

    如果你想以排序顺序漂亮地打印带有键的字典

    print(json.dumps(indent=4,sort_keys=True))
    {
        "Falcon": "discharged",
        "duck": "alive",
        "parrot": "dead",
        "penguin": "exploded"
    }
    

    【讨论】:

    • @AlxVallejo 您可能正在使用python3。请检查
    【解决方案7】:

    这很粗糙,但我只需要一种方法来可视化由任意 Mappings 和 Iterables 组成的数据结构,这就是我在放弃之前想出的。它是递归的,所以它会很好地通过嵌套结构和列表。我使用集合中的 Mapping 和 Iterable 抽象基类来处理任何事情。

    我的目标是使用简洁的 python 代码实现几乎类似 yaml 的输出,但没有成功。

    def format_structure(d, level=0):
        x = ""
        if isinstance(d, Mapping):
            lenk = max(map(lambda x: len(str(x)), d.keys()))
            for k, v in d.items():
                key_text = "\n" + " "*level + " "*(lenk - len(str(k))) + str(k)
                x += key_text + ": " + format_structure(v, level=level+lenk)
        elif isinstance(d, Iterable) and not isinstance(d, basestring):
            for e in d:
                x += "\n" + " "*level + "- " + format_structure(e, level=level+4)
        else:
            x = str(d)
        return x
    

    以及一些使用 OrderedDict 和 OrderedDict 列表的测试数据...(天哪,Python 非常需要 OrderedDict 文字...)

    d = OrderedDict([("main",
                      OrderedDict([("window",
                                    OrderedDict([("size", [500, 500]),
                                                 ("position", [100, 900])])),
                                   ("splash_enabled", True),
                                   ("theme", "Dark")])),
                     ("updates",
                      OrderedDict([("automatic", True),
                                   ("servers",
                                    [OrderedDict([("url", "http://server1.com"),
                                                  ("name", "Stable")]),
                                     OrderedDict([("url", "http://server2.com"),
                                                  ("name", "Beta")]),
                                     OrderedDict([("url", "http://server3.com"),
                                                  ("name", "Dev")])]),
                                   ("prompt_restart", True)])),
                     ("logging",
                      OrderedDict([("enabled", True),
                                   ("rotate", True)]))])
    
    print format_structure(d)
    

    产生以下输出:

       main: 
                   window: 
                             size: 
                                 - 500
                                 - 500
                         position: 
                                 - 100
                                 - 900
           splash_enabled: True
                    theme: Dark
    updates: 
                automatic: True
                  servers: 
                         - 
                              url: http://server1.com
                             name: Stable
                         - 
                              url: http://server2.com
                             name: Beta
                         - 
                              url: http://server3.com
                             name: Dev
           prompt_restart: True
    logging: 
           enabled: True
            rotate: True
    

    在使用 str.format() 进行更好对齐的过程中,我有一些想法,但不想深入研究它。您需要根据所需的对齐类型动态指定字段宽度,这会变得棘手或繁琐。

    无论如何,这以可读的分层方式向我展示了我的数据,所以这对我有用!

    【讨论】:

      【解决方案8】:
      def pprint_od(od):
          print "{"
          for key in od:
              print "%s:%s,\n" % (key, od[key]) # Fixed syntax
          print "}"
      

      你去吧^^

      for item in li:
          pprint_od(item)
      

      (pprint_od(item) for item in li)
      

      【讨论】:

      • 我正在寻找一种方法来拥有一个可以漂亮地打印 OrderedDicts 以及其他类型的函数。我不知道如何使用您的函数来漂亮地打印,比如说,OrderedDicts 列表。
      • -1 pprint_od() 函数不起作用 - for key, item in od 语句导致 ValueError: too many values to unpack 并且 唯一缩进的输出是最终的 " }"print 语句中的key, item 需要放在括号中。给你^^
      【解决方案9】:

      您也可以使用kzh 答案的这种简化:

      pprint(data.items(), indent=4)
      

      它保留了顺序,并且输出几乎与webwurst 答案相同(通过 json 转储打印)。

      【讨论】:

        【解决方案10】:

        我已经在 python3.5 上测试了这个基于猴子补丁的邪恶黑客,它可以工作:

        pprint.PrettyPrinter._dispatch[pprint._collections.OrderedDict.__repr__] = pprint.PrettyPrinter._pprint_dict
        
        
        def unsorted_pprint(data):
            def fake_sort(*args, **kwargs):
                return args[0]
            orig_sorted = __builtins__.sorted
            try:
                __builtins__.sorted = fake_sort
                pprint.pprint(data)
            finally:
                __builtins__.sorted = orig_sorted
        

        您让pprint 使用通常的基于 dict 的摘要,并在调用期间禁用排序,这样实际上就不会对键进行排序以进行打印。

        【讨论】:

        • 您也可以将pretty_print.py 复制为本地模块并对其进行破解(删除sorted 调用或任何您想要的)。
        【解决方案11】:

        从 Python 3.8 开始:pprint.PrettyPrinter 公开了 sort_dicts 关键字参数。

        True 默认情况下,将其设置为 False 将使字典不排序。

        >>> from pprint import PrettyPrinter
        
        >>> x = {'John': 1,
        >>>      'Mary': 2,
        >>>      'Paul': 3,
        >>>      'Lisa': 4,
        >>>      }
        
        >>> PrettyPrinter(sort_dicts=False).pprint(x)
        

        将输出:

        {'John': 1, 
         'Mary': 2, 
         'Paul': 3,
         'Lisa': 4}
        

        参考:https://docs.python.org/3/library/pprint.html

        【讨论】:

        • 它没有用。然后我意识到你说 “从 python 3.8 开始” 很遗憾,这是非常真实和非常重要的 ^_^。一个快速的解决方法是 manually download 来自较新 python 版本的最新 pprint 模块并使用它(假设它与您使用的任何 Python 版本兼容 - 我用 3.6 测试过)。
        • 问题是关于 OrderedDict 对象,而不是关于 dict 对象。
        【解决方案12】:

        pprint() 方法只是调用其中事物的 __repr__() 方法,而 OrderedDict 在它的方法中似乎没有做太多事情(或者没有一个或什么)。

        这是一个便宜的解决方案,应该可以工作如果您不关心 PPRINT 输出中的订单是否可见,如果:

        class PrintableOrderedDict(OrderedDict):
            def __repr__(self):
                return dict.__repr__(self)
        

        我真的很惊讶订单没有被保留......啊,好吧。

        【讨论】:

        • 一个python字典是使用hashmap实现的。因此,一旦将 OrderedDict(基本 dict 和保留顺序的列表的组合)转换为 dict,就会丢失任何顺序信息。此外,repr 方法应该返回一个字符串,该字符串表示 Python 代码中的对象。换句话说,obj == eval(repr(obj)),或者,至少是 repr(obj) == repr(eval(repr(obj)))。 OrderedDict 的 repr 做得很好。 dict.__repr__ 为您提供非常易于阅读的表示完全是 dict 文字('{' 和 '}' 等)的副作用。 OrderedDict 没有这个。
        【解决方案13】:

        较新版本的 Python 3 没有您提到的问题。当您pprint OrderedDict 时,如果有足够的键值对,它会将每个键值对包装在单独的行上,而不是包装:

        >>> 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)])
        

        Python 2.7 会将所有内容放在一行中,但 Python 3 不会。

        【讨论】:

          【解决方案14】:

          您可以重新定义pprint() 并拦截对OrderedDict 的调用。这是一个简单的例子。如所写,OrderedDict 覆盖代码忽略任何可选的 streamindentwidthdepth 关键字,这些关键字可能已被传递,但可以增强以实现它们。不幸的是,这种技术不能在另一个容器中处理它们,例如 listOrderDict

          from collections import OrderedDict
          from pprint import pprint as pp_pprint
          
          def pprint(obj, *args, **kwrds):
              if not isinstance(obj, OrderedDict):
                  # use stock function
                  return pp_pprint(obj, *args, **kwrds)
              else:
                  # very simple sample custom implementation...
                  print "{"
                  for key in obj:
                      print "    %r:%r" % (key, obj[key])
                  print "}"
          
          l = [10, 2, 4]
          d = dict((('john',1), ('paul',2), ('mary',3)))
          od = OrderedDict((('john',1), ('paul',2), ('mary',3)))
          pprint(l, width=4)
          # [10,
          #  2,
          #  4]
          pprint(d)
          # {'john': 1, 'mary': 3, 'paul': 2}
          
          pprint(od)
          # {
          #     'john':1
          #     'paul':2
          #     'mary':3
          # }
          

          【讨论】:

            【解决方案15】:

            对于python

            猴子补丁pprintsorted 以防止它被排序。 这也将具有递归工作的所有好处,并且比 json 选项更适合任何需要使用的人,例如width参数:

            import pprint
            pprint.sorted = lambda arg, *a, **kw: arg
            
            >>> pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
            {'z': 1,
             'a': 2,
             'c': {'z': 0,
                   'a': 1}}
            

            编辑:清理

            在这个肮脏的业务之后清理一下: pprint.sorted = sorted

            对于真正干净的解决方案,甚至可以使用上下文管理器:

            import pprint
            import contextlib
            
            @contextlib.contextmanager
            def pprint_ordered():
                pprint.sorted = lambda arg, *args, **kwargs: arg
                yield
                pprint.sorted = sorted
            
            # usage:
            
            with pprint_ordered():
                pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
            
            # without it    
            pprint.pprint({'z': 1, 'a': 2, 'c': {'z': 0, 'a': 1}}, width=20)
            
            # prints: 
            #    
            # {'z': 1,
            #  'a': 2,
            #  'c': {'z': 0,
            #        'a': 1}}
            #
            # {'a': 2,
            #  'c': {'a': 1,
            #        'z': 0},
            #  'z': 1}
            

            【讨论】:

            • 问题是关于 OrderedDict 对象,而不是关于 dict 对象。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-07-18
            • 2011-03-20
            • 2012-02-18
            • 2019-09-30
            • 1970-01-01
            • 2019-04-20
            • 2014-02-28
            相关资源
            最近更新 更多