красивая печать OrderedDicts с использованием pprint

Я использую pprint, чтобы красиво распечатать dict, и все работает нормально. Теперь я переключаюсь на использование OrderedDict из модуля collections. К сожалению, маршрутизация pprint, похоже, не распознает, что такие объекты также более или менее dict, и возвращается к печати в виде длинной строки.

>>> d = { i:'*'*i for i in range(8) }
>>> pprint.pprint(d)
{0: '',
 1: '*',
 2: '**',
 3: '***',
 4: '****',
 5: '*****',
 6: '******',
 7: '*******'}
>>> pprint.pprint(collections.OrderedDict(d))
OrderedDict([(0, ''), (1, '*'), (2, '**'), (3, '***'), (4, '****'), (5, '*****'), (6, '******'), (7, '*******')])

Есть ли способ получить более красивое представление OrderedDicts? Может быть, даже если они вложены внутри обычного dict или list?


person Alfe    schedule 29.01.2014    source источник


Ответы (4)


Я нашел относительно простое решение для этого, но оно включает в себя риск того, что вывод для вашего упорядоченного словаря будет выглядеть точно так же, как если бы это был обычный объект dict.

Исходное решение по использованию менеджера контекста для предотвращения pprint сортировки ключей словаря взято из этого ответа.

@contextlib.contextmanager
def pprint_OrderedDict():
    pp_orig = pprint._sorted
    od_orig = OrderedDict.__repr__
    try:
        pprint._sorted = lambda x:x
        OrderedDict.__repr__ = dict.__repr__
        yield
    finally:
        pprint._sorted = pp_orig
        OrderedDict.__repr__ = od_orig

(Вы также можете просто исправить метод OrderedDict.__repr__ с помощью dict.__repr__, но не делайте этого.)

Пример:

>>> foo = [('Roger', 'Owner'), ('Diane', 'Manager'), ('Bob', 'Manager'),
...        ('Ian', 'Associate'), ('Bill', 'Associate'), ('Melinda', 'Associate')]

>>> d = OrderedDict(foo)
>>> pprint.pprint(d)
OrderedDict([('Roger', 'Owner'), ('Diane', 'Manager'), ('Bob', 'Manager'), ('Ian', 'Associate'), ('Bill', 'Associate'), ('Melinda', 'Associate')])

>>> pprint.pprint(dict(d))
{'Bill': 'Associate',
 'Bob': 'Manager',
 'Diane': 'Manager',
 'Ian': 'Associate',
 'Melinda': 'Associate',
 'Roger': 'Owner'}

>>> with pprint_OrderedDict():
...     pprint.pprint(d)
...
{'Roger': 'Owner',
 'Diane': 'Manager',
 'Bob': 'Manager',
 'Ian': 'Associate',
 'Bill': 'Associate',
 'Melinda': 'Associate'}
person ForShame    schedule 22.12.2016

Попробуйте это на:

d = collections.OrderedDict({ i:'*'*i for i in range(8) })

ИЗМЕНИТЬ

pprint.pprint(list(d.items()))

person misakm    schedule 29.01.2014
comment
Да, хорошо, хорошо. Тогда это выглядит как список кортежей, а не как dict. Я надеялся на представление, которое все еще выглядело бы как диктофон (и, возможно, просто игнорировало тот факт, что оно было упорядоченным). - person Alfe; 29.01.2014
comment
Я думаю, вы даже можете снять list вокруг этих d.items(). Но я все еще удивляюсь, почему у pprint вообще такая проблема с OrderedDicts; ведь они наследуют dict. - person Alfe; 29.01.2014
comment
Ах, я думал, что не имеет значения, находится ли он в списке, основанном на «Может быть», даже если они вложены в обычный словарь или список? - person misakm; 29.01.2014
comment
Нет, это просто означало, что я также рассматриваю возможность красиво напечатать такие вещи, как { 3: OrderedDict(…) }. - person Alfe; 29.01.2014
comment
Я понимаю. Вне головы я не могу придумать другого способа сделать это. Почему бы не отказаться от использования pprint, пройти через OrderedDict и отформатировать вывод самостоятельно? - person misakm; 29.01.2014
comment
Я использую pprint для вывода большой древовидной структуры, и одна часть внутри нее — это OrderedDict. - person Alfe; 29.01.2014
comment
@Alfe: Это потому, что реализация pprint проверяет не только то, является ли объект dict (через isinstance), но также и то, имеет ли класс объекта (в данном случае OrderedDict) тот же метод __repr__, что и dict. Любые другие классы, которые наследуются от dict, но не переопределяют их метод __repr__, должны рассматриваться так же, как dict. - person musiphil; 19.09.2015

Если вы специально ориентируетесь на CPython* 3.6 или более позднюю версию, вы можете просто использовать обычные словари вместо OrderedDict. Вы упустите несколько методов, эксклюзивных для OrderedDict. , и это (пока) не гарантирует переносимость на другие реализации Python**, но это, вероятно, самый простой способ выполнить то, что вы пытаетесь сделать.

* CPython — это эталонная реализация Python, которую можно загрузить с сайта python.org.
** CPython украл эту идею у PyPy, так что вы, вероятно, можете положиться на то, что она работает и там.

person Kevin    schedule 22.12.2016
comment
Интересный! Но в настоящее время они говорят, что на него нельзя полагаться, пока спецификации языка не изменятся, что может произойти в будущем. Так хорошо, хм. - person Alfe; 25.12.2016

Я понимаю, что это своего рода некропостинг, но я решил опубликовать то, что использую. Его главное достоинство заключается в том, что его выходные данные могут быть прочитаны обратно в python, что позволяет, например, переключаться между представлениями (что я использую, например, в файлах JSON). Конечно, он нарушает инкапсуляцию pprint, отрывая некоторый код от своей внутренней функции _format.

#!/bin/env python
from __future__ import print_function

import pprint;
from collections import OrderedDict
import json
import sys


class MyPP (pprint.PrettyPrinter):
    def _format(self, object, stream, indent, allowance, context, level):
        if not isinstance(object, OrderedDict) :
            return pprint.PrettyPrinter._format(self, object, stream, indent, allowance, context, level)
        level = level + 1
        objid = id(object)
        if objid in context:
            stream.write(_recursion(object))
            self._recursive = True
            self._readable = False
            return
        write = stream.write
        _len=len
        rep = self._repr(object, context, level - 1)
        typ = type(object)
        sepLines = _len(rep) > (self._width - 1 - indent - allowance)

        if self._depth and level > self._depth:
            write(rep)
            return

        write('OrderedDict([\n%s'%(' '*(indent+1),))
        if self._indent_per_level > 1:
            write((self._indent_per_level - 1) * ' ')
        length = _len(object)
        #import pdb; pdb.set_trace()
        if length:
            context[objid] = 1
            indent = indent + self._indent_per_level
            items = object.items()
            key, ent = items[0]
            rep = self._repr(key, context, level)
            write('( ')
            write(rep)
            write(', ')
            self._format(ent, stream, indent + _len(rep) + 2,
                         allowance + 1, context, level)
            write(' )')
            if length > 1:
                for key, ent in items[1:]:
                    rep = self._repr(key, context, level)
                    if sepLines:
                        write(',\n%s( %s , ' % (' '*indent, rep))
                    else:
                        write(', ( %s , ' % rep)
                    self._format(ent, stream, indent + _len(rep) + 2,
                                 allowance + 1, context, level)
                    write(' )')

            indent = indent - self._indent_per_level
            del context[objid]
        write('])')
        return

pp = MyPP(indent=1)
handle=open(sys.argv[1],"r")
values=json.loads(handle.read(),object_pairs_hook=OrderedDict)
pp.pprint(values)
person Alien Life Form    schedule 03.10.2017