Any way to properly pretty-print OrderedDict?
I like the pprint module in Python. I use it a lot for testing and debugging. I frequently use the width option to make sure the output fits nicely within my terminal window.
I开发者_如何学JAVAt has worked fine until they added the new ordered dictionary type 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)])
Does anyone here have a way to make it print nicely, like the old unordered dictionaries? I could probably figure something out, possibly using the PrettyPrinter.format method, if I spend enough time, but I am wondering if anyone here already knows of a solution.
UPDATE: I filed a bug report for this. You can see it at http://bugs.python.org/issue10592.
Ever since Python 3.7, Python guarantees that keys in a dictionary will retain their insertion order. (They still don't behave exactly the same as OrderedDict
objects, though, as two dicts a
and b
can be considered equal a == b
even if the order of the keys is different, whereas OrderedDict
does check this upon testing for equality.)
Python 3.8 or newer:
You can use sort_dicts=False
to prevent it from sorting them alphabetically:
>>> 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 or older:
As a temporary workaround, you can try dumping in JSON format instead of using pprint
.
You lose some type information, but it looks nice and keeps the order.
>>> import json
>>> print(json.dumps(example_dict, indent=4))
{
"x": 1,
"b": 2,
"m": 3
}
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}
Ever since Python 3.7, Python guarantees that keys in a dictionary will retain their insertion order. So if you are using Python 3.7+, you don't need to make sure that your OrderedDict is alphabetically sorted.
Here's another answer that works by overriding and using the stock pprint()
function internally. Unlike my earlier one it will handle OrderedDict
's inside another container such as a list
and should also be able to handle any optional keyword arguments given — however it does not have the same degree of control over the output that the other one afforded.
It operates by redirecting the stock function's output into a temporary buffer and then word wraps that before sending it on to the output stream. While the final output produced isn't exceptionalily pretty, it's decent and may be "good enough" to use as a workaround.
Update 2.0
Simplified by using standard library textwrap
module, and modified to work in
both 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)))]
Sample output:
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)])]
To print an ordered dict, e.g.
from collections import OrderedDict
d=OrderedDict([
('a', OrderedDict([
('a1',1),
('a2','sss')
])),
('b', OrderedDict([
('b1', OrderedDict([
('bb1',1),
('bb2',4.5)])),
('b2',4.5)
])),
])
I do
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)
Which yields
"a": {
"a1": 1,
"a2": "sss"
},
"b": {
"b1": {
"bb1": 1,
"bb2": 4.5
},
"b2": 4.5
}
or
print dict_or_OrdDict_to_formatted_str(d, mode='OD')
which yields
("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
sorts the keys before printing, so to preserve order, we just have to make the keys sort in the way we want.
Note that this impacts the items()
function.
So you might want to preserve and restore the overridden functions after doing the 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'}
Here is my approach to pretty print an 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"
}
If you want to pretty print dictionary with keys in sorted order
print(json.dumps(indent=4,sort_keys=True))
{
"Falcon": "discharged",
"duck": "alive",
"parrot": "dead",
"penguin": "exploded"
}
This is pretty crude, but I just needed a way to visualize a data structure made up of any arbitrary Mappings and Iterables and this is what I came up with before giving up. It's recursive, so it will fall through nested structures and lists just fine. I used the Mapping and Iterable abstract base classes from collections to handle just about anything.
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
and some test data using OrderedDict and lists of OrderedDicts... (sheesh Python needs OrderedDict literals sooo badly...)
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)
yields the following output:
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
I had some thoughts along the way of using str.format() for better alignment, but didn't feel like digging into it. You'd need to dynamically specify the field widths depending on the type of alignment you want, which would get either tricky or cumbersome.
Anyway, this shows me my data in readable hierarchical fashion, so that works for me!
def pprint_od(od):
print "{"
for key in od:
print "%s:%s,\n" % (key, od[key]) # Fixed syntax
print "}"
There you go ^^
for item in li:
pprint_od(item)
or
(pprint_od(item) for item in li)
You can also use this simplification of the kzh answer:
pprint(data.items(), indent=4)
It preserves the order and will output almost the same than the webwurst answer (print through json dump).
I've tested this unholy monkey-patch based hack on python3.5 and it works:
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
You make pprint
use the usual dict based summary and also disable sorting for the duration of the call so that no keys are actually sorted for printing.
As of Python 3.8 : pprint.PrettyPrinter
exposes the sort_dicts
keyword parameter.
True by default, setting it to False will leave the dictionary unsorted.
>>> from pprint import PrettyPrinter
>>> x = {'John': 1,
>>> 'Mary': 2,
>>> 'Paul': 3,
>>> 'Lisa': 4,
>>> }
>>> PrettyPrinter(sort_dicts=False).pprint(x)
Will output :
{'John': 1,
'Mary': 2,
'Paul': 3,
'Lisa': 4}
Reference : https://docs.python.org/3/library/pprint.html
The pprint()
method is just invoking the __repr__()
method of things in it, and OrderedDict
doesn't appear to do much in it's method (or doesn't have one or something).
Here's a cheap solution that should work IF YOU DON'T CARE ABOUT THE ORDER BEING VISIBLE IN THE PPRINT OUTPUT, which may be a big if:
class PrintableOrderedDict(OrderedDict):
def __repr__(self):
return dict.__repr__(self)
I'm actually surprised that the order isn't preserved... ah well.
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 will put it all on one line, but Python 3 doesn't.
You could redefine pprint()
and intercept calls for OrderedDict
's. Here's a simple illustration. As written, the OrderedDict
override code ignores any optional stream
, indent
, width
, or depth
keywords that may have been passed, but could be enhanced to implement them. Unfortunately this technique doesn't handle them inside another container, such as a list
of OrderDict
's
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
# }
For python < 3.8 (e.g. 3.6):
Monkey patch pprint
's sorted
in order to prevent it from sorting.
This will have the benefit of everything working recursively as well, and is more suitable than the json
option for whoever needs to use e.g. width
parameter:
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}}
Edit: cleaning up
To clean up after this dirty business just run:
pprint.sorted = sorted
For a really clean solution can even use a contextmanager:
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}
精彩评论