มีวิธีใดในการพิมพ์พจนานุกรมที่สั่งพิมพ์ได้อย่างถูกต้อง?


97

ฉันชอบโมดูล pprint ใน Python ฉันใช้มันมากในการทดสอบและแก้ไขข้อบกพร่อง ฉันมักใช้ตัวเลือกความกว้างเพื่อให้แน่ใจว่าเอาต์พุตพอดีกับหน้าต่างเทอร์มินัลของฉัน

มันใช้งานได้ดีจนกว่าพวกเขาจะเพิ่มประเภทพจนานุกรมที่สั่งซื้อใหม่ใน Python 2.7 (คุณสมบัติที่ยอดเยี่ยมอีกอย่างที่ฉันชอบมาก) ถ้าฉันพยายามพิมพ์พจนานุกรมแบบเรียงลำดับสวย ๆ มันก็ไม่ได้แสดงอย่างสวยงาม แทนที่จะมีคู่คีย์ - ค่าแต่ละคู่ในบรรทัดของตัวเองสิ่งทั้งหมดจะแสดงเป็นบรรทัดยาวเส้นเดียวซึ่งตัดหลายครั้งและอ่านยาก

ใครที่นี่มีวิธีที่จะทำให้มันพิมพ์ออกมาได้ดีเหมือนพจนานุกรมที่ไม่มีการเรียงลำดับแบบเก่า ๆ ? ฉันอาจคิดอะไรบางอย่างออกโดยอาจใช้เมธอด PrettyPrinter.format ถ้าฉันใช้เวลาพอสมควร แต่ฉันสงสัยว่ามีใครรู้วิธีแก้ปัญหาอยู่แล้ว

UPDATE:ฉันยื่นรายงานข้อบกพร่องสำหรับสิ่งนี้ คุณสามารถดูได้ที่http://bugs.python.org/issue10592


2
แนะนำให้เพิ่มความคิดเห็นเกี่ยวกับพจนานุกรมที่สั่งซื้อไปที่bugs.python.org/issue7434
Ned Deily

คำตอบ:


133

วิธีแก้ปัญหาชั่วคราวคุณสามารถลองทิ้งในรูปแบบ JSON คุณสูญเสียข้อมูลบางประเภท แต่ก็ดูดีและรักษาความเป็นระเบียบ

import json

pprint(data, indent=4)
# ^ugly

print(json.dumps(data, indent=4))
# ^nice

7
@scottmrogowski ทำไมไม่เพียงpprint.pprint(dict(data))?
Alfe

2
pprint.pprint(dict(data))ทำงานได้ดีหากคุณไม่สนใจลำดับของคีย์ โดยส่วนตัวแล้วฉันหวังว่า__repr__for OrderedDictจะให้ผลลัพธ์เช่นนี้ แต่รักษาลำดับของคีย์ไว้
ws_e_c421

9
@Alfe ถ้าคำสั่งซ้อน OrderDicts พวกเขาจะไม่แสดงอย่างสวยงาม
Catskul

1
ยังล้มเหลวในจำนวนเต็มเป็นคีย์
DimmuR

2
@ อัลเฟ: เนื่องจากไม่ได้สั่งเอาต์พุต เหตุผลที่ OrderDict ถูกใช้แทนคำสั่งในตอนแรกเนื่องจากคำสั่งมีความสำคัญ
Teekin

15

สิ่งต่อไปนี้จะใช้งานได้หากลำดับของ OrderDict ของคุณเป็นแบบอัลฟ่าเนื่องจาก pprint จะเรียงลำดับคำสั่งก่อนพิมพ์

pprint(dict(o.items()))

2
เนื่องจาก OrderDicts ถูกเรียงลำดับตามลำดับการแทรกดังนั้นจึงอาจใช้กับการใช้งานเพียงเล็กน้อย โดยไม่คำนึงถึงการแปลง OD เป็นdictควรหลีกเลี่ยงปัญหาของการวางทุกอย่างไว้ในบรรทัดเดียว
martineau

9

นี่คืออีกคำตอบที่ใช้ได้โดยการลบล้างและใช้หุ้น pprint()ฟังก์ชันภายใน ซึ่งแตกต่างจากของฉันก่อนหน้านี้มันจะจัดการOrderedDictภายในภาชนะอื่นเช่นlistและควรสามารถจัดการอาร์กิวเมนต์คีย์เวิร์ดที่เป็นตัวเลือกที่กำหนดได้ - อย่างไรก็ตามมันไม่มีการควบคุมในระดับเดียวกันกับเอาต์พุตที่อีกอันหนึ่งจ่าย

มันทำงานโดยเปลี่ยนเส้นทางเอาต์พุตของฟังก์ชั่นหุ้นไปยังบัฟเฟอร์ชั่วคราวจากนั้นจึงตัดคำก่อนที่จะส่งไปยังสตรีมเอาต์พุต แม้ว่าผลลัพธ์สุดท้ายที่ผลิตออกมาจะไม่สวยโดดเด่น แต่ก็เหมาะสมและอาจ "ดีพอ" ที่จะใช้เป็นวิธีแก้ปัญหา

อัปเดต 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)])]


ฉันลองแล้วและได้ผล อย่างที่คุณบอกว่ามันไม่ได้สวยที่สุด แต่มันเป็นทางออกที่ดีที่สุดที่ฉันเคยเห็นมา
Elias Zamaria

7

ในการพิมพ์คำสั่งตามคำสั่งเช่น

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

5

นี่คือวิธีการที่ hacks 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'}

2
ดี แต่ดีกว่าที่จะพิมพ์ย่อยแล้วแทนที่ฟังก์ชัน
xmedeko

3

นี่คือแนวทางของฉันในการพิมพ์ OrderDict สวย ๆ

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

@AlxVallejo คุณอาจกำลังใช้python3. โปรดตรวจสอบ
CHINTAN VADGAMA

2

นี่ค่อนข้างหยาบ แต่ฉันแค่ต้องการวิธีที่จะเห็นภาพโครงสร้างข้อมูลที่ประกอบขึ้นจากการแมปและ Iterables โดยพลการและนี่คือสิ่งที่ฉันคิดขึ้นมาก่อนที่จะยอมแพ้ เป็นแบบวนซ้ำดังนั้นมันจะตกอยู่ในโครงสร้างที่ซ้อนกันและรายการที่ดี ฉันใช้คลาสพื้นฐานนามธรรมการทำแผนที่และทำซ้ำได้จากคอลเล็กชันเพื่อจัดการทุกอย่าง

ฉันตั้งเป้าไว้ที่เกือบจะเป็นผลงานที่มีโค้ด 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

และข้อมูลการทดสอบบางส่วนโดยใช้ OrderDict และรายการ OrderDicts ... (sheesh Python ต้องการตัวอักษร OrderDict แย่มาก ... )

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 () เพื่อการจัดตำแหน่งที่ดีขึ้น แต่ไม่รู้สึกอยากเจาะลึกลงไป คุณจะต้องระบุความกว้างของฟิลด์แบบไดนามิกโดยขึ้นอยู่กับประเภทของการจัดตำแหน่งที่คุณต้องการซึ่งอาจยุ่งยากหรือยุ่งยาก

อย่างไรก็ตามนี่แสดงข้อมูลของฉันในรูปแบบลำดับชั้นที่อ่านได้ซึ่งเหมาะกับฉัน!


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

ฉันกำลังมองหาวิธีที่จะมีฟังก์ชั่นหนึ่งที่สามารถพิมพ์ OrderDicts ได้สวยเช่นเดียวกับประเภทอื่น ๆ ฉันไม่เห็นว่าฉันจะใช้ฟังก์ชันของคุณในการพิมพ์สวย ๆ พูดรายการ OrderDicts ได้อย่างไร
Elias Zamaria

-1 pprint_od()ฟังก์ชั่นการทำงานไม่ได้ - The for key, item in odผลคำสั่งในValueError: too many values to unpack และเอาท์พุทเท่านั้นเยื้องเป็นครั้งสุดท้าย" }" และkey, itemในprintความต้องการที่จะเป็นคำสั่งในวงเล็บ เอาล่ะ ^^
martineau

2

ฉันได้ทดสอบแฮ็คที่ใช้แพทช์ลิงที่ไม่บริสุทธิ์นี้บน 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ใช้ข้อมูลสรุปตามคำสั่งตามปกติและปิดใช้งานการเรียงลำดับในช่วงเวลาของการโทรเพื่อไม่ให้มีการเรียงคีย์สำหรับการพิมพ์


นอกจากนี้คุณยังสามารถคัดลอกpretty_print.pyเป็นโมดูลภายในเครื่องและแฮ็กได้ (ลบการsortedโทรหรืออะไรก็ได้ที่คุณต้องการ)
Karl Rosaen

2

ตั้งแต่ 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


1

pprint()วิธีการเป็นเพียงการกล่าวอ้าง__repr__()วิธีการของสิ่งที่อยู่ในนั้นและOrderedDictไม่ปรากฏที่จะทำมากในวิธีการของมัน (หรือไม่ได้มีหนึ่งหรือบางอย่าง)

นี่เป็นวิธีการแก้ปัญหาราคาถูกที่ควรใช้หากคุณไม่สนใจคำสั่งซื้อที่มองเห็นได้ในเอาต์พุต PPRINTซึ่งอาจเป็นเรื่องใหญ่หาก:

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

ฉันแปลกใจจริงๆที่คำสั่งซื้อไม่ถูกเก็บรักษาไว้ ...


พจนานุกรมหลามถูกนำไปใช้โดยใช้แฮชแมป ดังนั้นเมื่อคุณแปลง OrderDict (การรวมกันของคำสั่งพื้นฐานและรายการเพื่อรักษาคำสั่งซื้อ) เป็นคำสั่งคุณจะสูญเสียข้อมูลการสั่งซื้อใด ๆ นอกจากนี้เมธอด repr ควรส่งคืนสตริงที่จะแสดงถึงอ็อบเจ็กต์ในโค้ด python กล่าวอีกนัยหนึ่ง obj == eval (repr (obj)) หรือที่ repr ขั้นต่ำ (obj) == repr (eval (repr (obj))) ตัวแทนของ OrderDict ทำสิ่งนี้ได้ดี dict .__ repr__ การให้ตัวแทนที่อ่านได้ของมนุษย์เป็นผลข้างเคียงอย่างสมบูรณ์ของตัวอักษรตามคำบอก ('{' and '}' ฯลฯ ) OrderDict ไม่มีสิ่งนี้
marr75

1

คุณยังสามารถใช้การทำให้เข้าใจง่ายของคำตอบkzh :

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

มันรักษาคำสั่งซื้อและจะให้ผลลัพธ์ใกล้เคียงกับคำตอบของwebwurst ( พิมพ์ผ่าน json dump )


1

สำหรับ python <3.8 (เช่น 3.6):

แพทช์ลิงpprint's 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

สำหรับวิธีการแก้ปัญหาที่สะอาดจริงๆสามารถใช้คอนเท็กซ์แมนเจอร์:

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}

0

คุณสามารถกำหนด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
# }

0

หากรายการพจนานุกรมเป็นประเภทเดียวทั้งหมดคุณสามารถใช้ไลบรารีการจัดการข้อมูลที่น่าทึ่ง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

2
รหัสนั้นจะทำอะไร? มีข้อดีอะไรบ้างจากโซลูชันอื่น ๆ ที่นี่?
Elias Zamaria
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.