有什么方法可以正确地打印“订单语录”吗?

我喜欢 Python 中的 pprint 模块。我经常用它来测试和调试。我经常使用 width 选项来确保输出在我的终端窗口中非常合适。

It has worked fine until they added the new 有序字典型式 in Python 2.7 (another cool feature I really like). If I try to pretty-print an ordered dictionary, it doesn't show nicely. Instead of having each key-value pair on its own line, the whole thing shows up on one long line, which wraps many times and is hard to read:

>>> 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看到它。

80728 次浏览
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)

The following will work if the order of your OrderedDict is an alpha sort, since pprint will sort a dict before print.

>>> 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 保证 dictionary 中的键将保留其插入顺序。因此,如果您正在使用 Python 3.7 + ,则不需要确保 OrderedDect 是按字母顺序排序的。

您可以重新定义 pprint()并拦截 OrderedDict的呼叫。这里有一个简单的例子。根据编写的代码,OrderedDict覆盖代码忽略任何可选的 streamindentwidthdepth关键字,这些关键字可能已经传递,但可以通过增强来实现它们。不幸的是,这种技术不能在另一个容器中处理它们,例如 OrderDictlist

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
# }

下面是另一个通过在内部重写并使用股票 pprint()函数来工作的答案。与我的 早些时候不同,它的 will处理 OrderedDict内的另一个容器,如 list,也应该能够处理给定的任何可选的关键字参数ーー然而,它对输出的控制程度不同于另一个提供的控制程度。

它的操作方式是将 stock 函数的输出重定向到一个临时缓冲区,然后在将其发送到输出流之前对其进行字包装。虽然最终产生的输出并不是特别漂亮,但它还不错,可能“足够好”,可以作为一种解决方案使用。

更新2.0

通过使用标准库 textwrap模块进行简化,并修改为在 Python 2和 Python 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)])] < br >

自 Python 3.7以来,Python 保证 dictionary 中的键将保留其插入顺序。(尽管如此,它们的行为仍然与 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()方法只是调用其中事物的 __repr__()方法,而 OrderedDict在它的方法中似乎没有多少功能(或者没有一个或者什么东西)。

这里有一个廉价的解决方案,应该工作的 如果你不在乎打印输出中的订单,这可能是一个很大的如果:

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

事实上,我很惊讶这个命令没有被保存下来。

这非常粗糙,但我只是需要一种方法来可视化一个由任意映射和可迭代组成的数据结构,这就是我放弃之前想到的。它是递归的,所以通过嵌套结构和列表就可以了。我使用了来自集合的 Mapping 和 Iterable 抽象基类来处理几乎所有事情。

I was aiming for almost yaml like output with concise python code, but didn't quite make it.

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 和 OrderedDicts 列表的测试数据... ... (sheeshPython 非常需要 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 ()进行更好的对齐的过程中,我有一些想法,但是不想深入研究。您需要根据所需的对齐类型动态地指定字段宽度,这将变得棘手或繁琐。

无论如何,这显示了我的数据在可读的层次结构的方式,所以这为我工作!

打印有序的字典,例如。

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

Here’s a way that hacks the implementation of 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'}

你也可以使用这个简化的 谢谢答案:

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

它保留了顺序,并将输出几乎相同于 韦伯香肠的答案(通过 Json dump 打印出来的)。

下面是我打印 OrderedDect 的方法

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"
}

我已经在 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 的汇总,并且还禁用调用期间的排序,以便没有键被实际排序用于打印。

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

默认情况下,True ,将其设置为 假的将使字典无序。

>>> 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(例如3.6) :

猴子补丁 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}

Newer versions of Python 3 do not have the problem you are mentioning. When you pprint an OrderedDict, it does wrap each key-value pair on a separate line instead of wrapping, if there are enough key-value pairs:

>>> 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不会。