Adakah cara untuk mencetak kamus yang teratur dengan baik?

97

Saya suka modul pprint dengan Python. Saya sering menggunakannya untuk pengujian dan debugging. Saya sering menggunakan opsi lebar untuk memastikan keluarannya pas dengan jendela terminal saya.

Ini berfungsi dengan baik sampai mereka menambahkan jenis kamus yang dipesan baru dengan Python 2.7 (fitur keren lain yang sangat saya suka). Jika saya mencoba untuk mencetak kamus yang dipesan dengan cantik, itu tidak ditampilkan dengan baik. Alih-alih menempatkan setiap pasangan nilai kunci di barisnya sendiri, semuanya muncul dalam satu baris panjang, yang membungkus berkali-kali dan sulit dibaca.

Apakah ada orang di sini yang memiliki cara untuk membuatnya tercetak dengan baik, seperti kamus lama yang tidak berurutan? Saya mungkin bisa menemukan sesuatu, mungkin menggunakan metode PrettyPrinter.format, jika saya menghabiskan cukup waktu, tetapi saya bertanya-tanya apakah ada orang di sini yang sudah mengetahui solusinya.

UPDATE: Saya mengajukan laporan bug untuk ini. Anda bisa melihatnya di http://bugs.python.org/issue10592 .

Elias Zamaria
sumber
2
Sarankan menambahkan komentar tentang kamus yang dipesan ke bugs.python.org/issue7434
Ned Deily

Jawaban:

132

Sebagai solusi sementara, Anda dapat mencoba membuang dalam format JSON. Anda kehilangan beberapa jenis informasi, tapi kelihatannya bagus dan menjaga ketertiban.

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice
webwurst.dll
sumber
7
@scottmrogowski Mengapa tidak begitu saja pprint.pprint(dict(data))?
Alfe
2
pprint.pprint(dict(data))berfungsi dengan baik jika Anda tidak peduli dengan urutan kunci. Secara pribadi, saya berharap __repr__for OrderedDictakan menghasilkan output seperti ini tetapi mempertahankan urutan kuncinya.
ws_e_c421
9
@Alfe jika dict telah menumpuk OrderedDicts, mereka tidak akan ditampilkan dengan baik
Catskul
1
Juga gagal pada integer sebagai kunci
DimmuR
2
@Alfe: Karena outputnya tidak dipesan. Alasan mengapa OrderedDict digunakan daripada dict di tempat pertama, adalah karena urutan itu penting.
Teekin
15

Berikut ini akan berfungsi jika urutan OrderedDict Anda adalah alfa, karena pprint akan mengurutkan dict sebelum dicetak.

pprint(dict(o.items()))
kzh
sumber
2
Karena OrderedDicts diurutkan berdasarkan urutan penyisipan, jadi ini mungkin berlaku untuk sebagian kecil penggunaan. Terlepas dari itu, mengubah OD menjadi a dictharus menghindari masalah segala sesuatu ditempatkan pada satu baris.
martineau
8

Berikut jawaban lain yang bekerja dengan mengesampingkan dan menggunakan pprint()fungsi stok secara internal. Tidak seperti yang saya sebelumnya, ia akan menangani OrderedDictdi dalam wadah lain seperti a listdan juga harus dapat menangani setiap argumen kata kunci opsional yang diberikan - namun itu tidak memiliki tingkat kontrol yang sama atas keluaran yang diberikan yang lain.

Ini beroperasi dengan mengarahkan output fungsi stok ke buffer sementara dan kemudian kata membungkusnya sebelum mengirimnya ke aliran output. Meskipun hasil akhir yang dihasilkan tidak luar biasa cantik, ini layak dan mungkin "cukup baik" untuk digunakan sebagai solusi.

Perbarui 2.0

Disederhanakan dengan menggunakan textwrapmodul pustaka standar , dan dimodifikasi untuk bekerja dengan 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)))]

Output sampel:

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.dll
sumber
Saya mencobanya dan berhasil. Seperti yang Anda katakan, ini bukan yang tercantik, tetapi ini adalah solusi terbaik yang pernah saya lihat sejauh ini.
Elias Zamaria
7

Untuk mencetak dikt yang dipesan, mis

from collections import OrderedDict

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

Saya lakukan

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)

Hasil yang mana

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

atau

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

yang menghasilkan

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

Berikut cara yang meretas implementasi pprint. pprintmengurutkan kunci sebelum mencetak, jadi untuk menjaga ketertiban, kita hanya perlu membuat kunci menyortir sesuai keinginan kita.

Perhatikan bahwa ini memengaruhi items()fungsi. Jadi, Anda mungkin ingin mempertahankan dan memulihkan fungsi yang diganti setelah melakukan pencetakan.

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
sumber
2
Bagus, tetapi lebih baik untuk subtipe daripada mengganti fungsi.
xmedeko
3

Inilah pendekatan saya untuk cukup mencetak 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"
}

Jika Anda ingin cukup mencetak kamus dengan kunci dalam urutan yang diurutkan

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}
CHINTAN VADGAMA
sumber
@AlxVallejo Anda mungkin menggunakan python3. Silakan periksa
CHINTAN VADGAMA
2

Ini cukup kasar, tetapi saya hanya perlu cara untuk memvisualisasikan struktur data yang terdiri dari Pemetaan dan Iterabel sembarang dan inilah yang saya temukan sebelum menyerah. Ini rekursif, jadi itu akan jatuh melalui struktur dan daftar bersarang dengan baik. Saya menggunakan kelas dasar Mapping dan abstrak Iterable dari koleksi untuk menangani apa saja.

Saya bertujuan untuk menghasilkan output yang hampir seperti yaml dengan kode python yang ringkas, tetapi tidak cukup berhasil.

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

dan beberapa data pengujian menggunakan OrderedDict dan daftar OrderedDicts ... (Sheesh Python sangat membutuhkan literal 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)

menghasilkan keluaran berikut:

   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

Saya memiliki beberapa pemikiran selama menggunakan str.format () untuk penyelarasan yang lebih baik, tetapi tidak merasa ingin menggalinya. Anda perlu menentukan lebar bidang secara dinamis tergantung pada jenis perataan yang Anda inginkan, yang akan menjadi rumit atau tidak praktis.

Bagaimanapun, ini menunjukkan kepada saya data saya dalam mode hierarki yang dapat dibaca, jadi itu berhasil untuk saya!

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

Ini dia ^^

for item in li:
    pprint_od(item)

atau

(pprint_od(item) for item in li)
Jakob Bowyer
sumber
Saya mencari cara untuk memiliki satu fungsi yang dapat mencetak OrderedDicts serta tipe lainnya dengan cantik. Saya tidak melihat bagaimana saya akan menggunakan fungsi Anda untuk mencetak cantik, katakanlah, daftar OrderedDicts.
Elias Zamaria
-1 pprint_od()fungsi tidak bekerja - for key, item in odhasil pernyataan dalam ValueError: too many values to unpack dan satu-satunya keluaran menjorok adalah final " }" dan yang key, itemdalam printkebutuhan pernyataan berada di kurung. Ini dia ^^
martineau
2

Saya telah menguji peretasan berbasis tambalan monyet yang tidak suci ini di python3.5 dan berhasil:

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

Anda pprintmenggunakan ringkasan berbasis dict biasa dan juga menonaktifkan pengurutan selama panggilan sehingga tidak ada kunci yang benar-benar diurutkan untuk dicetak.

Karl Rosaen
sumber
Anda juga dapat menyalin pretty_print.pysebagai modul lokal dan meretasnya (menghapus sortedpanggilan atau apa pun yang Anda inginkan).
Karl Rosaen
2

Pada Python 3.8: pprint.PrettyPrintermengekspos filesort_dicts parameter kata kunci.

True secara default, menyetelnya ke False akan membuat kamus tidak diurutkan.

>>> from pprint import PrettyPrinter

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

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

Akan menghasilkan:

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

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

mxdbld.dll
sumber
1

The pprint()Metode hanya menyerukan __repr__()metode hal di dalamnya, danOrderedDict tidak muncul untuk berbuat banyak dalam metode itu (atau tidak memiliki satu atau sesuatu).

Berikut adalah solusi murah yang seharusnya bekerja JIKA ANDA TIDAK PEDULI DENGAN PESANAN YANG TERLIHAT DI OUTPUT PPRINT , yang mungkin besar jika:

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

Saya benar-benar terkejut bahwa pesanan tidak dipertahankan ... ah baiklah.

Bill M.
sumber
Kamus python diimplementasikan menggunakan hashmap. Oleh karena itu, setelah Anda mengonversi OrderedDict (kombinasi dari dict dasar dan daftar untuk mempertahankan pesanan) menjadi sebuah dict, Anda kehilangan informasi pesanan apa pun. Selanjutnya, metode repr diharapkan mengembalikan string yang akan mewakili objek dalam kode python. Dengan kata lain, obj == eval (repr (obj)), atau, minimal repr (obj) == repr (eval (repr (obj))). Repr OrderedDict melakukan ini dengan baik. dict .__ repr__ yang memberi Anda representasi yang sangat manusiawi adalah efek samping dari dict literal ('{' and '}', dll.). OrderedDict tidak memiliki ini.
marr75
1

Anda juga dapat menggunakan penyederhanaan jawaban kzh ini :

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

Ini mempertahankan urutan dan akan menghasilkan hampir sama dengan jawaban webwurst ( cetak melalui json dump ).

Albert Alomar
sumber
1

Untuk python <3.8 (misalnya 3.6):

Monyet Patch pprintIni sorteduntuk mencegah dari penyortiran. Ini akan mendapatkan keuntungan dari semua yang bekerja secara rekursif juga, dan lebih cocok daripada jsonopsi untuk siapa pun yang perlu menggunakan misalnya widthparameter:

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

Edit: bersih-bersih

Untuk membersihkan setelah bisnis kotor ini jalankan saja: pprint.sorted = sorted

Untuk solusi yang benar-benar bersih bahkan dapat menggunakan pengelola konteks:

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

Anda dapat mendefinisikan ulang pprint()dan mencegat panggilan untuk OrderedDict. Berikut ilustrasi sederhana. Seperti ditulis, OrderedDictkode menimpa mengabaikan setiap opsional stream, indent, width, atau depthkata kunci yang mungkin telah berlalu, namun dapat ditingkatkan untuk melaksanakannya. Sayangnya teknik ini tidak menangani mereka di dalam wadah lain, seperti listdari 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.dll
sumber
0

Jika item kamus semuanya dari satu jenis, Anda dapat menggunakan pustaka penanganan data yang menakjubkan pandas:

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

atau

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object
LondonRob
sumber
2
Apa yang akan dilakukan kode itu? Keuntungan apa yang dimilikinya dibandingkan solusi lain di sini?
Elias Zamaria