Gibt es eine Möglichkeit, bestellte Wörterbücher richtig auszudrucken?

97

Ich mag das pprint-Modul in Python. Ich benutze es oft zum Testen und Debuggen. Ich verwende häufig die Option width, um sicherzustellen, dass die Ausgabe gut in mein Terminalfenster passt.

Es hat gut funktioniert, bis sie den neuen geordneten Wörterbuchtyp in Python 2.7 hinzugefügt haben (eine weitere coole Funktion, die ich wirklich mag). Wenn ich versuche, ein bestelltes Wörterbuch hübsch auszudrucken, wird es nicht gut angezeigt. Anstatt jedes Schlüssel-Wert-Paar in einer eigenen Zeile zu haben, wird das Ganze in einer langen Zeile angezeigt, die viele Male umbrochen wird und schwer zu lesen ist.

Hat hier jemand eine Möglichkeit, es schön zu drucken, wie die alten ungeordneten Wörterbücher? Ich könnte wahrscheinlich etwas herausfinden, möglicherweise mithilfe der PrettyPrinter.format-Methode, wenn ich genug Zeit verbringe, aber ich frage mich, ob hier jemand bereits eine Lösung kennt.

UPDATE: Ich habe dafür einen Fehlerbericht eingereicht. Sie können es unter http://bugs.python.org/issue10592 sehen .

Elias Zamaria
quelle
2
Schlagen Sie vor, einen Kommentar zum bestellten Wörterbuch zu bugs.python.org/issue7434
Ned Deily

Antworten:

131

Als vorübergehende Problemumgehung können Sie versuchen, ein Dumping im JSON-Format durchzuführen. Sie verlieren einige Typinformationen, aber es sieht gut aus und behält die Reihenfolge bei.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
Webwurst
quelle
7
@scottmrogowski Warum nicht einfach pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))funktioniert gut, wenn Sie sich nicht um die Reihenfolge der Schlüssel kümmern. Persönlich wünschte ich mir, das __repr__for OrderedDictwürde eine solche Ausgabe erzeugen, aber die Reihenfolge der Schlüssel beibehalten.
ws_e_c421
9
@ Alfe, wenn das Diktat OrderedDicts verschachtelt hat, werden sie nicht schön angezeigt
Catskul
1
Scheitert
2
@ Alfe: Weil dann die Ausgabe nicht geordnet ist. Der Grund, warum OrderedDict anstelle von dict verwendet wird, liegt darin, dass die Reihenfolge wichtig ist.
Teekin
15

Das Folgende funktioniert, wenn die Reihenfolge Ihres OrderedDict eine Alpha-Sortierung ist, da pprint vor dem Drucken ein Diktat sortiert.

pprint(dict(o.items()))
kzh
quelle
2
Da OrderedDicts nach Einfügereihenfolge sortiert sind, gilt dies wahrscheinlich für einen kleinen Prozentsatz der Verwendungen. Unabhängig davon sollte beim Konvertieren des OD in a dictvermieden werden, dass alles in einer Zeile steht.
Martineau
8

Hier ist eine weitere Antwort, die funktioniert, indem die Bestandsfunktion pprint()intern überschrieben und verwendet wird . Im Gegensatz zu meinem früher man es wird handhaben OrderedDict‚s in einem anderen Behälter, wie ein listund soll auch gegeben alle optionales Schlüsselwort Argumente verarbeiten kann - aber es nicht das gleiche Maß an Kontrolle über die Ausgabe hat , dass der andere gewährt.

Dabei wird die Ausgabe der Bestandsfunktion in einen temporären Puffer umgeleitet und anschließend mit einem Wort umbrochen, bevor sie an den Ausgabestream weitergeleitet wird. Die endgültige Ausgabe ist zwar nicht besonders hübsch, aber anständig und möglicherweise "gut genug", um sie zu umgehen.

Update 2.0

Vereinfacht durch Verwendung des Standardbibliotheksmoduls textwrapund geändert, um sowohl in Python 2 als auch in Python 3 zu funktionieren.

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

Beispielausgabe:

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
quelle
Ich habe das versucht und es funktioniert. Wie Sie sagten, ist es nicht das Schönste, aber es ist die beste Lösung, die ich bisher gesehen habe.
Elias Zamaria
7

So drucken Sie ein bestelltes Diktat, z

from collections import OrderedDict

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

ich mache

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)

Welche ergibt

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

oder

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

was ergibt

("a", OrderedDict([
    ("a1", 1),
    ("a2", "sss")
])),
("b", OrderedDict([
    ("b1", OrderedDict([
        ("bb1", 1),
        ("bb2", 4.5)
    ])),
    ("b2", 4.5)
]))
Ilya Prokin
quelle
5

Hier ist ein Weg, der die Implementierung von hackt pprint. pprintsortiert die Schlüssel vor dem Drucken. Um die Reihenfolge zu erhalten, müssen wir die Schlüssel nur so sortieren, wie wir es möchten.

Beachten Sie, dass dies Auswirkungen auf die items()Funktion hat. Daher möchten Sie möglicherweise die überschriebenen Funktionen nach dem Drucken beibehalten und wiederherstellen.

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
quelle
2
Schön, aber besser zu unterschreiben als Funktionen zu überschreiben.
Xmedeko
3

Hier ist mein Ansatz, um ein OrderedDict hübsch zu drucken

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

Wenn Sie ein Wörterbuch mit Schlüsseln in sortierter Reihenfolge hübsch drucken möchten

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
quelle
@AlxVallejo Möglicherweise verwenden Sie python3. Bitte überprüfen Sie
CHINTAN VADGAMA
2

Das ist ziemlich grob, aber ich brauchte nur eine Möglichkeit, eine Datenstruktur zu visualisieren, die aus beliebigen Mappings und Iterables besteht, und das habe ich mir ausgedacht, bevor ich aufgegeben habe. Es ist rekursiv, so dass es durch verschachtelte Strukturen und Listen fällt. Ich habe die abstrakten Basisklassen Mapping und Iterable aus Sammlungen verwendet, um fast alles zu handhaben.

Ich strebte eine fast yamlartige Ausgabe mit prägnantem Python-Code an, schaffte es aber nicht ganz.

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

und einige Testdaten mit OrderedDict und Listen von OrderedDicts ... (Meine Güte, Python braucht sooo dringend OrderedDict-Literale ...)

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)

ergibt folgende Ausgabe:

   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

Ich hatte einige Gedanken auf dem Weg, str.format () für eine bessere Ausrichtung zu verwenden, hatte aber keine Lust, mich damit zu beschäftigen. Sie müssen die Feldbreiten abhängig von der gewünschten Ausrichtung dynamisch angeben, was entweder schwierig oder umständlich wird.

Auf jeden Fall zeigt mir dies meine Daten in lesbarer hierarchischer Weise, so dass das für mich funktioniert!

Flötenbruch7
quelle
2
def pprint_od(od):
    print "{"
    for key in od:
        print "%s:%s,\n" % (key, od[key]) # Fixed syntax
    print "}"

Los geht's ^^

for item in li:
    pprint_od(item)

oder

(pprint_od(item) for item in li)
Jakob Bowyer
quelle
Ich suche nach einer Möglichkeit, eine Funktion zu haben, mit der OrderedDicts und andere Typen hübsch gedruckt werden können. Ich sehe nicht ein, wie ich Ihre Funktion verwenden würde, um beispielsweise eine Liste von OrderedDicts hübsch auszudrucken.
Elias Zamaria
-1 Die pprint_od()Funktion funktioniert nicht - die for key, item in odAnweisung führt zu einem ValueError: too many values to unpack und die einzige eingerückte Ausgabe ist die endgültige " }" und die key, itemin der printAnweisung muss in Klammern stehen. Los geht's ^^
Martineau
2

Ich habe diesen unheiligen Affen-Patch-basierten Hack auf Python3.5 getestet und er funktioniert:

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

Sie pprintverwenden die übliche diktbasierte Zusammenfassung und deaktivieren die Sortierung für die Dauer des Anrufs, sodass keine Schlüssel zum Drucken sortiert werden.

Karl Rosaen
quelle
Sie können das pretty_print.pyModul auch einfach als lokales Modul kopieren und hacken ( sortedAnruf entfernen oder was auch immer Sie wollen).
Karl Rosaen
2

Ab Python 3.8: pprint.PrettyPrintermacht den sort_dictsSchlüsselwortparameter verfügbar.

Wenn Sie den Wert standardmäßig auf " Falsch" setzen, bleibt das Wörterbuch unsortiert.

>>> from pprint import PrettyPrinter

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

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

Wird ausgegeben:

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

Referenz: https://docs.python.org/3/library/pprint.html

Fett gedruckt
quelle
1

Die pprint()Methode ruft nur die __repr__()Methode der darin enthaltenen Dinge auf und OrderedDictscheint in ihrer Methode nicht viel zu tun (oder hat keine oder etwas).

Hier ist eine billige Lösung, die funktionieren sollte, wenn Sie sich nicht darum kümmern , dass die Bestellung im PPRINT-Ausgang sichtbar ist. Dies kann eine große Herausforderung sein , wenn:

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

Ich bin tatsächlich überrascht, dass die Reihenfolge nicht erhalten bleibt ... na ja.

Bill M.
quelle
Ein Python-Wörterbuch wird mithilfe einer Hashmap implementiert. Wenn Sie also ein OrderedDict (Kombination aus einem einfachen Diktat und einer Liste, um die Reihenfolge beizubehalten) in ein Diktat konvertieren, verlieren Sie alle Bestellinformationen. Darüber hinaus soll die repr-Methode eine Zeichenfolge zurückgeben, die das Objekt im Python-Code darstellt. Mit anderen Worten, obj == eval (repr (obj)) oder zumindest repr (obj) == repr (eval (repr (obj))). Der Repräsentant von OrderedDict macht das ganz gut. dict .__ repr__, das Ihnen eine sehr gut lesbare Darstellung gibt, ist ein Nebeneffekt des dict-Literal ('{' und '}' usw.). OrderedDict hat das nicht.
Marr75
1

Sie können auch diese Vereinfachung der kzh- Antwort verwenden:

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

Es behält die Reihenfolge bei und gibt fast die gleiche Ausgabe aus wie die Webwurst- Antwort ( Drucken über JSON-Dump ).

Albert Alomar
quelle
1

Für Python <3,8 (zB 3,6):

Affen-Patches pprint, sortedum das Sortieren zu verhindern. Dies hat den Vorteil, dass alles auch rekursiv funktioniert, und ist besser geeignet als die jsonOption für jeden, der zB widthParameter verwenden muss:

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

Bearbeiten: Aufräumen

Um nach diesem schmutzigen Geschäft aufzuräumen, führen Sie einfach Folgendes aus: pprint.sorted = sorted

Für eine wirklich saubere Lösung kann sogar ein Kontextmanager verwendet werden:

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
quelle
0

Sie können pprint()Anrufe für OrderedDict's neu definieren und abfangen . Hier ist eine einfache Illustration. Wie geschrieben, die OrderedDictignoriert Aufhebungscode alle optionalen stream, indent, width, oder depthSchlüsselwörter , die weitergegeben worden sein, könnte aber verbessert werden , um sie umzusetzen. Leider werden sie mit dieser Technik nicht in einem anderen Container wie einem listvon OrderDict's behandelt

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
quelle
0

Wenn die Wörterbuchelemente alle von einem Typ sind, können Sie die erstaunliche Datenverarbeitungsbibliothek verwenden pandas:

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

oder

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object
LondonRob
quelle
2
Was wird dieser Code tun? Welche Vorteile hat es gegenüber den anderen Lösungen hier?
Elias Zamaria