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


97

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

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

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

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


2
Предложите добавить комментарий об упорядоченном словаре на bugs.python.org/issue7434
Нед Дейли,

Ответы:


133

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

import json

pprint(data, indent=4)
# ^ugly

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

7
@scottmrogowski Почему не просто pprint.pprint(dict(data))?
Alfe

2
pprint.pprint(dict(data))работает хорошо, если вам не важен порядок клавиш. Лично я хочу, чтобы __repr__for OrderedDictпроизводил такой вывод, но с сохранением порядка ключей.
ws_e_c421

9
@Alfe, если в dict есть вложенные OrderedDicts, они не будут отображаться
должным образом

1
Также не работает с целыми числами в качестве ключей
DimmuR

2
@Alfe: Потому что тогда вывод не упорядочен. Причина, по которой OrderedDict используется вместо dict в первую очередь, заключается в том, что порядок имеет значение.
Teekin

15

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

pprint(dict(o.items()))

2
Поскольку OrderedDicts упорядочены по порядку вставки, это, вероятно, относится к небольшому проценту использования. Тем не менее, преобразование OD в a dictдолжно избежать проблемы размещения всего в одной строке.
Мартино

8

Вот еще один ответ, который работает путем pprint()внутреннего переопределения и использования функции акций . В отличие от моего более раннего, он будет обрабатывать OrderedDictвнутри другого контейнера, такого как a, 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)])]


Я пробовал это, и это работает. Как вы сказали, это не самое красивое, но лучшее решение, которое я когда-либо видел.
Элиас Замария 01

7

Чтобы напечатать заказанный 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'}

2
Красиво, но лучше подтипировать, а не переопределять функции.
xmedeko

3

Вот мой подход к красивой печати 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. Пожалуйста, проверьте
ЧИНТАН ВАДГАМА

2

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

Во всяком случае, это показывает мне мои данные в читаемой иерархической форме, так что это работает для меня!


2
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 и единственный выход отступа является окончательной " }" иkey, item в printнеобходимости заявления , чтобы быть в круглых скобках. Вот так ^^
Мартино

2

Я протестировал этот нечестивый взлом на основе обезьяньего патча на 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вызов или что угодно).
Карл Розаен

2

Начиная с 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


1

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

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

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

Я действительно удивлен, что порядок не соблюдается ... ну ладно.


Словарь Python реализован с использованием хэш-карты. Следовательно, как только вы конвертируете OrderedDict (комбинацию базового dict и списка для сохранения порядка) в dict, вы теряете любую информацию о заказе. Кроме того, предполагается, что метод repr возвращает строку, которая будет представлять объект в коде Python. Другими словами, obj == eval (repr (obj)) или, как минимум, repr (obj) == repr (eval (repr (obj))). Repr OrderedDict отлично с этим справляется. dict .__ repr__, дающий вам очень удобочитаемое представление, полностью является побочным эффектом литерала dict ('{' и '}' и т. д.). В OrderedDict этого нет.
marr75

1

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

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

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


1

Для python <3,8 (например, 3,6):

Обезьяна патч pprint«S 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}

0

Вы можете переопределить 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
# }

0

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

2
Что будет делать этот код? Какие преимущества здесь перед другими решениями?
Элиас Замария
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.