Jest jakiś sposób, aby poprawnie wydrukować uporządkowane słowniki?

97

Podoba mi się moduł pprint w Pythonie. Często go używam do testowania i debugowania. Często używam opcji szerokości, aby upewnić się, że wyjście dobrze pasuje do mojego okna terminala.

Działało dobrze, dopóki nie dodali nowego uporządkowanego typu słownika w Pythonie 2.7 (kolejna fajna funkcja, którą bardzo lubię). Jeśli spróbuję dobrze wydrukować uporządkowany słownik, nie wyświetla się on ładnie. Zamiast umieszczać każdą parę klucz-wartość w osobnym wierszu, całość pojawia się w jednym długim wierszu, który zawija się wiele razy i jest trudny do odczytania.

Czy ktoś tutaj ma sposób, aby ładnie wydrukować, jak stare nieuporządkowane słowniki? Prawdopodobnie mógłbym coś wymyślić, prawdopodobnie używając metody PrettyPrinter.format, jeśli spędzę wystarczająco dużo czasu, ale zastanawiam się, czy ktoś tutaj zna już rozwiązanie.

AKTUALIZACJA: Złożyłem w tej sprawie raport o błędzie. Możesz to zobaczyć pod adresem http://bugs.python.org/issue10592 .

Elias Zamaria
źródło
2
Proponuję dodanie komentarza o uporządkowanej słownika do bugs.python.org/issue7434
Ned Deily

Odpowiedzi:

133

W ramach tymczasowego obejścia możesz spróbować zrzucić zawartość w formacie JSON. Tracisz informacje o typie, ale wygląda to ładnie i zachowuje kolejność.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
webwurst
źródło
7
@scottmrogowski Dlaczego nie po prostu pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))działa dobrze, jeśli nie zależy Ci na kolejności kluczy. Osobiście chciałbym, żeby __repr__for OrderedDictdziałał w ten sposób, ale zachował kolejność kluczy.
ws_e_c421
9
@Alfe, jeśli dykt ma zagnieżdżone OrderedDicts, nie będą ładnie wyświetlane
Catskul
1
Nie udaje się również na liczbach całkowitych jako kluczach
DimmuR
2
@Alfe: Ponieważ wtedy wyjście nie jest uporządkowane. Powodem, dla którego używany jest OrderedDict zamiast dict w pierwszej kolejności, jest to, że kolejność ma znaczenie.
Teekin
15

Poniższe będą działać, jeśli kolejność twojego OrderedDict jest sortowaniem alfa, ponieważ pprint posortuje dict przed wydrukowaniem.

pprint(dict(o.items()))
kzh
źródło
2
Ponieważ OrderedDicts są sortowane według kolejności reklamowej, prawdopodobnie dotyczy to niewielkiego odsetka zastosowań. Niezależnie od tego, konwersja OD na a dictpowinna uniknąć problemu umieszczania wszystkiego w jednej linii.
martineau
8

Oto kolejna odpowiedź, która działa, zastępując i używając pprint()wewnętrznie funkcji giełdy . W przeciwieństwie do mojego wcześniejszego , będzie obsługiwał OrderedDictzawartość innego kontenera, takiego jak a, listi powinien być w stanie obsłużyć wszelkie podane opcjonalne argumenty słów kluczowych - jednak nie ma takiego samego stopnia kontroli nad danymi wyjściowymi, jak ten drugi.

Działa na zasadzie przekierowania wyjścia funkcji stock do tymczasowego bufora, a następnie zawija je przed wysłaniem do strumienia wyjściowego. Chociaż ostateczny wynik nie jest wyjątkowo ładny, jest przyzwoity i może być „wystarczająco dobry”, aby można go było użyć jako obejścia.

Aktualizacja 2.0

Uproszczony przy użyciu standardowego textwrapmodułu bibliotecznego i zmodyfikowany do pracy w Pythonie 2 i 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)))]

Przykładowe dane wyjściowe:

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

martineau
źródło
Próbowałem tego i działa. Jak powiedziałeś, nie jest to najładniejsze, ale jest to najlepsze rozwiązanie, jakie do tej pory widziałem.
Elias Zamaria
7

Aby wydrukować zamówiony dykt, np

from collections import OrderedDict

d=OrderedDict([
    ('a', OrderedDict([
        ('a1',1),
        ('a2','sss')
    ])),
    ('b', OrderedDict([
        ('b1', OrderedDict([
            ('bb1',1),
            ('bb2',4.5)])),
        ('b2',4.5)
    ])),
])

ja robię

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)

Co daje

"a": {
    "a1": 1,
    "a2": "sss"
},
"b": {
    "b1": {
        "bb1": 1,
        "bb2": 4.5
    },
    "b2": 4.5
}

lub

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

która daje

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))
Ilya Prokin
źródło
5

Oto sposób, w jaki hakuje implementację pprint. pprintsortuje klucze przed wydrukowaniem, więc aby zachować porządek, musimy tylko uporządkować klucze w dowolny sposób.

Zwróć uwagę, że ma to wpływ na items()funkcję. Możesz więc chcieć zachować i przywrócić nadpisane funkcje po wykonaniu polecenia 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'}
rumpel
źródło
2
Ładnie, ale lepiej jest podtypować niż przesłonić funkcje.
xmedeko
3

Oto moje podejście do ładnego wydrukowania 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"
}

Jeśli chcesz ładnie wydrukować słownik z kluczami w posortowanej kolejności

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
źródło
@AlxVallejo Być może używasz python3. Proszę sprawdzić
CHINTAN VADGAMA
2

To dość prymitywne, ale potrzebowałem tylko sposobu na wizualizację struktury danych składającej się z dowolnych mapowań i elementów iterowalnych i właśnie to wymyśliłem, zanim się poddałem. Jest rekurencyjny, więc dobrze przechodzi przez zagnieżdżone struktury i listy. Użyłem abstrakcyjnych klas bazowych Mapping i Iterable z kolekcji do obsługi prawie wszystkiego.

Miałem na celu uzyskanie prawie yamlowego wyjścia ze zwięzłym kodem Pythona, ale nie do końca mi się to udało.

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

i trochę danych testowych przy użyciu OrderedDict i list OrderedDicts ... (sheesh Python bardzo potrzebuje literałów 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)

daje następujący wynik:

   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

Miałem kilka myśli po drodze użycia str.format () w celu lepszego wyrównania, ale nie miałem ochoty się w to zagłębiać. Konieczne byłoby dynamiczne określanie szerokości pól w zależności od typu wyrównania, które chcesz, co byłoby trudne lub uciążliwe.

W każdym razie to pokazuje mi moje dane w czytelny sposób hierarchiczny, więc to działa dla mnie!

flutefreak7
źródło
2
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

Proszę bardzo ^^

for item in li:
    pprint_od(item)

lub

(pprint_od(item) for item in li)
Jakob Bowyer
źródło
Szukam sposobu, aby mieć jedną funkcję, która może ładnie drukować OrderedDicts, a także inne typy. Nie wiem, jak użyłbym twojej funkcji do ładnego wydrukowania, powiedzmy, listy OrderedDicts.
Elias Zamaria
-1 pprint_od()funkcja nie działa - to for key, item in odwyniki instrukcja w ValueError: too many values to unpack i jedyne wyjście wcięty jest ostateczna " }" ikey, item w printpotrzebie oświadczeniu, które zostanie w nawiasach. Proszę bardzo ^^
martineau
2

Przetestowałem ten przeklęty hack oparty na małpach na pythonie 3.5 i działa:

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

Używasz pprintzwykłego podsumowania opartego na dyktowaniu, a także wyłączasz sortowanie na czas trwania połączenia, aby żadne klucze nie były faktycznie sortowane do drukowania.

Karl Rosaen
źródło
możesz również po prostu skopiować pretty_print.pyjako moduł lokalny i zhakować go (usunąć sortedpołączenie lub cokolwiek chcesz).
Karl Rosaen,
2

Od Pythona 3.8: pprint.PrettyPrinterudostępnia sort_dictsparametr słowa kluczowego.

Domyślnie prawda , ustawienie wartości False spowoduje, że słownik będzie nieposortowany.

>>> from pprint import PrettyPrinter

>>> x = {'John': 1,
>>>      'Mary': 2,
>>>      'Paul': 3,
>>>      'Lisa': 4,
>>>      }

>>> PrettyPrinter(sort_dicts=False).pprint(x)

Wyświetli:

{'John': 1, 
 'Mary': 2, 
 'Paul': 3,
 'Lisa': 4}

Źródła: https://docs.python.org/3/library/pprint.html

mxdbld
źródło
1

pprint()Metoda jest po prostu wywołując __repr__()metodę rzeczy w nim, a OrderedDictnie wydaje się zrobić wiele w jego metodzie (lub nie posiada jedną lub coś).

Oto tanie rozwiązanie, które powinno zadziałać, JEŚLI NIE DBASZ O KOLEJNOŚĆ WIDOCZNĄ NA WYJŚCIU PPRINT , co może być duże, jeśli:

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

Właściwie jestem zaskoczony, że zamówienie nie jest zachowane ... no cóż.

Bill M.
źródło
Słownik Pythona jest zaimplementowany za pomocą hashmap. Dlatego po przekonwertowaniu OrderedDict (kombinacji podstawowego dyktu i listy w celu zachowania porządku) na dyktę, utracisz wszelkie informacje o zamówieniu. Ponadto metoda repr ma zwrócić ciąg znaków, który reprezentowałby obiekt w kodzie Pythona. Innymi słowy, obj == eval (repr (obj)) lub co najmniej repr (obj) == repr (eval (repr (obj))). Repr z OrderedDict robi to dobrze. dict .__ repr__, który daje bardzo czytelną dla człowieka reprezentację, jest całkowicie efektem ubocznym tego słowa („{” i „}”, itp.). OrderedDict tego nie ma.
marr75
1

Możesz również użyć tego uproszczenia odpowiedzi kzh :

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

Zachowuje kolejność i wyświetla prawie to samo, co odpowiedź Webwurst ( drukowanie przez zrzut json ).

Albert Alomar
źródło
1

Dla Pythona <3,8 (np. 3,6):

Monkey patch pprintma sortedna celu zapobieżenie sortowaniu. Będzie to miało tę zaletę, że wszystko działa również rekurencyjnie i jest bardziej odpowiednie niż jsonopcja dla każdego, kto potrzebuje np. widthParametru:

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

Edycja: sprzątanie

Aby posprzątać po tym brudnym biznesie, po prostu uruchom: pprint.sorted = sorted

Aby uzyskać naprawdę czyste rozwiązanie, można nawet użyć menedżera kontekstu:

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}
artd
źródło
0

Możesz przedefiniować pprint()i przechwytywać połączenia OrderedDict. Oto prosta ilustracja. Jak napisane, OrderedDictkod przesłanianie ignoruje wszelkie opcjonalne stream, indent, widthlub depthsłowa kluczowe, które mogą być przekazywane, ale może być zwiększona w celu ich wdrożenia. Niestety technika ta nie obsługuje je w innym pojemniku, takim jak listz 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
# }
martineau
źródło
0

Jeśli wszystkie elementy słownika są jednego typu, możesz skorzystać z niesamowitej biblioteki do obsługi danych pandas:

>>> import pandas as pd
>>> x = {'foo':1, 'bar':2}
>>> pd.Series(x)
bar    2
foo    1
dtype: int64

lub

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object
LondonRob
źródło
2
Co zrobi ten kod? Jakie ma zalety w porównaniu z innymi rozwiązaniami tutaj?
Elias Zamaria