Будь-який спосіб правильно роздрукувати впорядковані словники?


97

Мені подобається модуль pprint в Python. Я багато використовую для тестування та налагодження. Я часто використовую опцію ширини, щоб переконатися, що вивід добре вписується в моє вікно терміналу.

Він працював нормально, поки вони не додали новий впорядкований тип словника в Python 2.7 (ще одна чудова функція, яка мені дуже подобається). Якщо я спробую достатньо надрукувати замовлений словник, це не буде добре виглядати. Замість того, щоб мати кожну пару ключ-значення у своєму рядку, все це відображається на одному довгому рядку, який обгортається багато разів і важко читається.

Хтось тут має спосіб зробити так, щоб він друкувався гарно, як старі невпорядковані словники? Я, певно, міг щось розібратися, можливо, використовуючи метод PrettyPrinter.format, якщо витрачу достатньо часу, але мені цікаво, чи хтось тут уже знає рішення.

ОНОВЛЕННЯ: Я подав звіт про помилку для цього. Ви можете побачити це на http://bugs.python.org/issue10592 .


2
Запропонуйте додати коментар щодо замовленого словника до bugs.python.org/issue7434
Нед Дейлі

Відповіді:


132

Як тимчасове вирішення проблеми ви можете спробувати звалити файл у форматі JSON. Ви втрачаєте певну інформацію про тип, але вона виглядає добре і зберігає замовлення.

import json

pprint(data, indent=4)
# ^ugly

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

7
@scottmrogowski Чому не просто pprint.pprint(dict(data))?
Альфе

2
pprint.pprint(dict(data))добре працює, якщо ви не дбаєте про порядок клавіш. Особисто я хотів би, щоб __repr__for OrderedDictвидавав подібні результати, але зберігав порядок ключів.
ws_e_c421

9
@Alfe, якщо dict вклав OrderedDicts, вони не будуть відображатися красиво
Catskul

1
Також не вдається використовувати цілі числа як ключі
DimmuR

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

15

Наступне буде працювати, якщо порядок вашого OrderedDict - альфа-сортування, оскільки pprint сортує dict перед друком.

pprint(dict(o.items()))

2
Оскільки OrderedDicts упорядковуються за порядком вставки, то це, ймовірно, стосується невеликого відсотка використання. Незважаючи на це, перетворення OD його на a dictповинно уникати проблеми розміщення одного рядка.
martineau

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


Я спробував це, і це працює. Як ви вже сказали, це не найкрасивіше, але це найкраще рішення, яке я бачив досі.
Elias Zamaria

7

Щоб надрукувати замовлений дикт, напр

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()функцію. Тож ви можете захотіти зберегти та відновити перевизначені функції після виконання відбитка.

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 ... (sheesh 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використовуєте звичайний підсумок на основі дикту, а також вимикаєте сортування на час дзвінка, щоб фактично не сортувалися клавіші для друку.


Ви також можете просто скопіювати pretty_print.pyяк локальний модуль і зламати його (видаливши sortedвиклик або все, що хочете).
Karl Rosaen,

2

Станом на Python 3.8: pprint.PrettyPrinterвиставляє sort_dictsпараметр ключового слова.

Істинно за замовчуванням, встановивши значення 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чи не здається , робити в його методі (або не має один або що - то).

Ось дешеве рішення, яке мало б працювати, ЯКЩО ВАМ НЕ ДБУТЬ ПРО ПОРЯДОК, ЯКИЙ БУДЕ ВИДИМИЙ В РЕЗУЛЬТАТІ ПЕЧАТИ , що може бути великим, якщо:

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

Я насправді здивований, що порядок не зберігся ... ну добре.


Словник python реалізований за допомогою хеш-карти. Отже, як тільки ви перетворюєте OrderedDict (поєднання основного та переліку для збереження порядку) у dict, ви втрачаєте будь-яку інформацію про замовлення. Крім того, метод repr повинен повертати рядок, який представляв би об'єкт у коді python. Іншими словами, obj == eval (repr (obj)), або, як мінімум repr (obj) == repr (eval (repr (obj))). ReprDD OrdersDict робить це просто чудово. dict .__ repr__, що дає вам зрозуміле людське подання, є повністю побічним ефектом буквального слова dict ('{' та '}' тощо). У OrriedDict цього немає.
marr75

1

Ви також можете скористатися цим спрощенням відповіді kzh :

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

Він зберігає порядок і видає майже те ж саме, що і відповідь webwurst ( друк через дамп json ).


1

Для пітона <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

Для дійсно чистого рішення можна навіть використовувати контекстний менеджер:

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.