Любой способ правильно напечатать упорядоченные словари?

Мне нравится модуль pprint в Python. Я использую его для тестирования и отладки. Я часто использую параметр ширины, чтобы убедиться, что выход хорошо вписывается в мое окно терминала.

Он отлично работал, пока не добавил новый упорядоченный тип словаря в Python 2.7 (еще одна интересная функция, которая мне очень нравится). Если я попытаюсь напечатать упорядоченный словарь, он не будет хорошо отображаться. Вместо того, чтобы иметь каждую пару ключевых значений в своей собственной строке, все это проявляется на одной длинной строке, которая многократно обертывается и ее трудно читать.

У кого-нибудь есть способ сделать его печатным, как старые неупорядоченные словари? Вероятно, я мог бы что-то выяснить, возможно, используя метод PrettyPrinter.format, если я потрачу достаточно времени, но мне интересно, знает ли кто-нибудь об этом решение.

ОБНОВЛЕНИЕ: Я написал для этого отчет об ошибке. Вы можете увидеть его на http://bugs.python.org/issue10592.

Ответ 1

В качестве временного обходного пути вы можете попробовать демпинг в формате JSON. Вы теряете информацию о типе, но он выглядит красиво и сохраняет порядок.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice

Ответ 2

Следующее будет работать, если порядок вашего OrderedDict является альфа-сортировкой, так как pprint будет сортировать файл перед печатью.

pprint(dict(o.items()))

Ответ 3

Вот еще один ответ, который работает путем переопределения и использования функции запаса pprint() внутри. В отличие от моего более раннего он будет обрабатывать OrderedDict внутри другого контейнера, такого как list, а также должен обрабатывать любые необязательные аргументы ключевого слова, но это не имеют ту же степень контроля над выходом, что и другой.

Он работает, перенаправляя вывод функции запаса во временный буфер, а затем обертывание слов, прежде чем отправлять его в выходной поток. Хотя конечный результат производства не является исключительно красивым, он приличный и может быть "достаточно хорошим" для использования в качестве обходного пути.

Обновление 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)])]

Ответ 4

Чтобы напечатать упорядоченный dict, например

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

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)

Ответ 7

Это довольно грубо, но мне просто нужен способ визуализировать структуру данных, состоящую из любых произвольных Mappings и Iterables, и это то, что я придумал, прежде чем сдаться. Он рекурсивный, поэтому он будет попадать вложенные структуры и списки просто отлично. Я использовал базовые классы 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... (sheesh Python нуждается в литералах OrderedDict 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)

выводит следующий результат:

   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

Я протестировал этот нечестивый хак на основе 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, а также отключаете сортировку на время вызова, чтобы никакие ключи фактически не сортировались для печати.

Ответ 9

Метод pprint() просто вызывает метод __repr__() вещей в нем, а OrderedDict, похоже, мало что делает в этом методе (или не имеет одного или что-то).

Здесь дешевое решение, которое должно работать, ЕСЛИ ВЫ НЕ УКАЗАНЫ О ЗАКАЗЕ, ВИДИМЫМ В ПРЯМОЙ ВЫХОДЕ, которая может быть большой, если:

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

Я действительно удивлен тем, что заказ не сохранился... хорошо.

Ответ 10

Вы также можете использовать это упрощение ответа kzh:

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

Он сохраняет порядок и выводит почти то же самое, что и webwurst (печатать через json dump).

Ответ 11

Вот мой подход к печати 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"
}

Ответ 12

Вы можете переопределить pprint() и перехватить вызовы для OrderedDict. Вот простая иллюстрация. Как написано, код переопределения OrderedDict игнорирует любые необязательные ключевые слова stream, indent, width или depth, которые могут быть переданы, но могут быть расширены для их реализации. К сожалению, этот метод не обрабатывает их внутри другого контейнера, например list OrderDict

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

Ответ 13

Если элементы словаря имеют один тип, вы можете использовать потрясающую библиотеку обработки данных 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