Есть ли способ правильно распечатать заказанные словари?

Мне нравится модуль 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}

Ссылка: https://docs.python.org/3/library/pprint.html

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

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

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

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

Для 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
Другие вопросы по тегам