Есть ли способ правильно распечатать заказанные словари?
Мне нравится модуль pprint в Python. Я часто использую его для тестирования и отладки. Я часто использую параметр ширины, чтобы убедиться, что выходные данные хорошо вписываются в мое окно терминала.
Он работал нормально, пока не добавили новый упорядоченный тип словаря в Python 2.7 (еще одна интересная функция, которая мне действительно нравится). Если я попытаюсь напечатать заказанный словарь, он не будет отображаться. Вместо того, чтобы каждая пара ключ-значение находилась в отдельной строке, все это отображается в одной длинной строке, которая многократно переносится и ее трудно прочитать.
У кого-нибудь здесь есть способ заставить его печатать красиво, как старые неупорядоченные словари? Я мог бы что-то выяснить, возможно, используя метод PrettyPrinter.format, если бы я потратил достаточно времени, но мне интересно, если кто-нибудь здесь уже знает о решении.
ОБНОВЛЕНИЕ: я подал отчет об ошибке для этого. Вы можете увидеть это на http://bugs.python.org/issue10592.
16 ответов
В качестве временного решения вы можете попробовать создать дамп в формате JSON. Вы теряете некоторую информацию о типе, но она выглядит красиво и сохраняет порядок.
import json
pprint(data, indent=4)
# ^ugly
print(json.dumps(data, indent=4))
# ^nice
Следующее будет работать, если порядок вашего OrderedDict - альфа-сортировка, так как pprint будет сортировать dict перед печатью.
pprint(dict(o.items()))
Вот еще один ответ, который работает путем переопределения и использования акций pprint()
функционировать внутри. В отличие от моего предыдущего он будет обрабатывать OrderedDict
внутри другого контейнера, такого как list
и должен также иметь возможность обрабатывать любые необязательные аргументы ключевого слова - однако он не имеет такой же степени контроля над выводом, как другой.
Он работает, перенаправляя вывод функции 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)])]
Чтобы распечатать заказанный документ, например,
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)
]))
Вот способ, который взламывает реализацию 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'}
Вот мой подход к печати 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"
}
Начиная с 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}
Это довольно грубо, но мне просто нужен был способ визуализации структуры данных, состоящей из любых произвольных отображений и итераций, и это то, что я придумал, прежде чем отказаться. Он рекурсивный, поэтому он будет просто проходить через вложенные структуры и списки. Я использовал абстрактные базовые классы Mapping и Iterable из коллекций для обработки практически всего.
Я стремился к почти yaml-подобному выводу с лаконичным кодом на Python, но не совсем это сделал.
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... (Sheeh Python нуждается в литералах OrderedDict sooo ужасно...)
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() для лучшего выравнивания, но мне не хотелось копаться в этом. Вам нужно было бы динамически указывать ширину поля в зависимости от типа выравнивания, который вы хотите, что будет сложно или громоздко.
Во всяком случае, это показывает мне мои данные в удобочитаемой иерархической форме, так что это работает для меня!
Я протестировал этот нечестивый хак на основе 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
используйте обычное основанное на диктовке резюме, а также отключите сортировку на время вызова, чтобы никакие ключи фактически не сортировались для печати.
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)
Для python < 3,8 (например, 3,6):
Патч обезьяны pprint
с sorted
чтобы предотвратить его сортировку. Это будет иметь то преимущество, что все будет работать рекурсивно, и это более подходит, чем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
Для действительно чистого решения можно даже использовать 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}
pprint()
Метод просто вызывает __repr__()
метод вещей в нем, и OrderedDict
Похоже, не так много в этом методе (или не имеет ничего или что-то).
Вот дешевое решение, которое должно работать, ЕСЛИ ВЫ НЕ ЗАБЫВАЕТЕ, ЧТО ОКАЗЫВАЕТСЯ ВИДИМОСТЬ В ВЫХОДЕ ПЕЧАТИ, что может быть большим, если:
class PrintableOrderedDict(OrderedDict):
def __repr__(self):
return dict.__repr__(self)
Я на самом деле удивлен, что порядок не сохранился... ну, хорошо.
В более новых версиях 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 - нет.
Вы могли бы переопределить pprint()
и перехватывать звонки для OrderedDict
"S. Вот простая иллюстрация. Как написано, OrderedDict
код переопределения игнорирует любой необязательный stream
, indent
, width
, или же depth
ключевые слова, которые могут быть переданы, но могут быть улучшены для их реализации. К сожалению, этот метод не обрабатывает их внутри другого контейнера, такого как list
из 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
# }
Если бы все элементы словаря были одного типа, вы могли бы использовать удивительную библиотеку обработки данных pandas
:
>>> import pandas as pd
>>> x = {'foo':1, 'bar':2}
>>> pd.Series(x)
bar 2
foo 1
dtype: int64
или же
>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz bam
foo bar
dtype: object