Có cách nào để in các từ điển được sắp xếp theo thứ tự đẹp mắt không?


97

Tôi thích mô-đun pprint bằng Python. Tôi sử dụng nó rất nhiều để thử nghiệm và gỡ lỗi. Tôi thường xuyên sử dụng tùy chọn chiều rộng để đảm bảo đầu ra vừa vặn trong cửa sổ đầu cuối của tôi.

Nó đã hoạt động tốt cho đến khi họ thêm loại từ điển có thứ tự mới trong Python 2.7 (một tính năng thú vị khác mà tôi thực sự thích). Nếu tôi cố in một cuốn từ điển có thứ tự, nó không hiển thị đẹp. Thay vì có mỗi cặp khóa-giá trị trên một dòng riêng của nó, toàn bộ nội dung hiển thị trên một dòng dài, lặp lại nhiều lần và khó đọc.

Có ai ở đây có cách làm cho nó in đẹp, giống như những cuốn từ điển cũ không có thứ tự không? Tôi có lẽ có thể tìm ra điều gì đó, có thể bằng cách sử dụng phương pháp PrettyPrinter.format, nếu tôi dành đủ thời gian, nhưng tôi đang tự hỏi liệu có ai ở đây đã biết giải pháp hay không.

CẬP NHẬT: Tôi đã gửi báo cáo lỗi cho việc này. Bạn có thể xem nó tại http://bugs.python.org/issue10592 .


2
Đề xuất thêm nhận xét về từ điển có thứ tự vào bug.python.org/issue7434
Ned Deily

Câu trả lời:


132

Như một giải pháp tạm thời, bạn có thể thử kết xuất ở định dạng JSON. Bạn mất một số thông tin loại, nhưng nó trông đẹp và giữ được thứ tự.

import json

pprint(data, indent=4)
# ^ugly

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

7
@scottmrogowski Tại sao không đơn giản pprint.pprint(dict(data))?
Alfe

2
pprint.pprint(dict(data))hoạt động tốt nếu bạn không quan tâm đến thứ tự của các phím. Cá nhân tôi mong muốn __repr__for OrderedDictsẽ tạo ra đầu ra như thế này nhưng bảo toàn thứ tự của các phím.
ws_e_c421

9
@Alfe nếu dict có OrderedDicts lồng nhau họ sẽ không được hiển thị độc đáo
Catskul

1
Cũng không thành công trên các số nguyên như phím
DimmuR

2
@Alfe: Vì khi đó đầu ra không có thứ tự. Lý do mà OrderedDict được sử dụng thay vì dict ngay từ đầu, là vì thứ tự quan trọng.
Teekin

15

Cách sau sẽ hoạt động nếu thứ tự của OrderedDict của bạn là một loại alpha, vì pprint sẽ sắp xếp một chính tả trước khi in.

pprint(dict(o.items()))

2
Vì các OrderedDicts được sắp xếp theo thứ tự chèn, vì vậy điều này có thể áp dụng cho một tỷ lệ nhỏ sử dụng. Bất kể, việc chuyển đổi OD sang a dictsẽ tránh được vấn đề mọi thứ được đặt trên một dòng.
martineau

8

Đây là một câu trả lời khác hoạt động bằng cách ghi đè và sử dụng pprint()hàm cổ phiếu trong nội bộ. Không giống như cái trước đó của tôi, nó sẽ xử lý OrderedDictbên trong một vùng chứa khác chẳng hạn như a listvà cũng có thể xử lý bất kỳ đối số từ khóa tùy chọn nào được đưa ra - tuy nhiên nó không có cùng mức độ kiểm soát đối với đầu ra mà cái kia đã có.

Nó hoạt động bằng cách chuyển hướng đầu ra của hàm chứng khoán thành một bộ đệm tạm thời và sau đó kết thúc từ đó trước khi gửi nó đến luồng đầu ra. Mặc dù kết quả cuối cùng được tạo ra không quá đẹp nhưng nó khá ổn và có thể "đủ tốt" để sử dụng như một giải pháp thay thế.

Cập nhật 2.0

Được đơn giản hóa bằng cách sử dụng textwrapmô-đun thư viện tiêu chuẩn và được sửa đổi để hoạt động trong cả 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)))]

Đầu ra mẫu:

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


Tôi đã thử điều đó và nó hoạt động. Như bạn đã nói, nó không phải là đẹp nhất, nhưng nó là giải pháp tốt nhất mà tôi thấy cho đến nay.
Elias Zamaria

7

Để in một mệnh lệnh có thứ tự, ví dụ:

from collections import OrderedDict

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

tôi làm

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)

Kết quả nào

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

hoặc là

print dict_or_OrdDict_to_formatted_str(d, mode='OD')

cái nào mang lại

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

5

Đây là một cách hack việc triển khai pprint. pprintsắp xếp các khóa trước khi in, vì vậy để giữ trật tự, chúng ta chỉ cần sắp xếp các khóa theo cách chúng ta muốn.

Lưu ý rằng điều này ảnh hưởng đến items()chức năng. Vì vậy, bạn có thể muốn bảo tồn và khôi phục các chức năng bị ghi đè sau khi thực hiện 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
Tốt, nhưng tốt hơn nên nhập kiểu con sau đó ghi đè các chức năng.
xmedeko

3

Đây là cách tiếp cận của tôi để in đẹp một 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"
}

Nếu bạn muốn in từ điển đẹp với các phím được sắp xếp theo thứ tự

print(json.dumps(indent=4,sort_keys=True))
{
    "Falcon": "discharged",
    "duck": "alive",
    "parrot": "dead",
    "penguin": "exploded"
}

@AlxVallejo Bạn có thể đang sử dụng python3. Vui lòng kiểm tra
CHINTAN VADGAMA 23/09/19

2

Điều này khá thô thiển, nhưng tôi chỉ cần một cách để hình dung một cấu trúc dữ liệu được tạo thành từ bất kỳ Ánh xạ và Lặp lại tùy ý nào và đây là những gì tôi đã nghĩ ra trước khi từ bỏ. Nó đệ quy, vì vậy nó sẽ rơi qua các cấu trúc và danh sách lồng nhau. Tôi đã sử dụng các lớp cơ sở trừu tượng Ánh xạ và Lặp lại từ các bộ sưu tập để xử lý mọi thứ.

Tôi đã nhắm đến đầu ra gần như yaml với mã python ngắn gọn, nhưng không thực hiện được.

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

và một số dữ liệu thử nghiệm bằng cách sử dụng OrderedDict và danh sách các OrderedDicts ... (sheesh Python cần các chữ OrderedDict quá tệ ...)

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)

tạo ra kết quả sau:

   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

Tôi đã có một số suy nghĩ về cách sử dụng str.format () để căn chỉnh tốt hơn, nhưng không cảm thấy muốn đào sâu vào nó. Bạn cần chỉ định động độ rộng trường tùy thuộc vào kiểu căn chỉnh bạn muốn, điều này sẽ phức tạp hoặc phức tạp.

Dù sao, điều này hiển thị cho tôi dữ liệu của tôi theo kiểu phân cấp có thể đọc được, vì vậy điều đó phù hợp với tôi!


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

Của bạn đây ^^

for item in li:
    pprint_od(item)

hoặc là

(pprint_od(item) for item in li)

Tôi đang tìm kiếm một số cách để có một chức năng có thể in đẹp các Lệnh theo thứ tự cũng như các loại khác. Tôi không hiểu làm thế nào tôi sẽ sử dụng chức năng của bạn để in đẹp, chẳng hạn, một danh sách các Hành động có Thứ tự.
Elias Zamaria

-1 Các pprint_od()chức năng không làm việc - những for key, item in odkết quả tuyên bố trong một ValueError: too many values to unpack đầu ra chỉ thụt vào là trận chung kết " }" các key, itemtrong printnhu cầu tuyên bố là trong ngoặc đơn. Bạn đi rồi ^^
martineau

2

Tôi đã thử nghiệm bản hack dựa trên bản vá khỉ xấu xa này trên python3.5 và nó hoạt động:

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

Bạn pprintsử dụng bản tóm tắt dựa trên chính tả thông thường và cũng tắt tính năng sắp xếp trong suốt thời gian cuộc gọi để không có phím nào được sắp xếp thực sự để in.


bạn cũng có thể chỉ cần sao chép pretty_print.pydưới dạng mô-đun cục bộ và xâm nhập vào nó (xóa sortedcuộc gọi hoặc bất cứ điều gì bạn muốn).
Karl Rosaen

2

Kể từ Python 3.8: pprint.PrettyPrinterhiển thị sort_dictstham số từ khóa.

Đúng theo mặc định, việc đặt thành Sai sẽ khiến từ điển không được sắp xếp.

>>> from pprint import PrettyPrinter

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

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

Sẽ xuất:

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

Tham khảo: https://docs.python.org/3/library/pprint.html


1

Các pprint()phương pháp chỉ là cách gọi các __repr__()phương pháp của sự vật trong nó, và OrderedDictkhông xuất hiện để làm được gì nhiều trong phương pháp của nó (hoặc không có một hoặc một cái gì đó).

Đây là một giải pháp giá rẻ sẽ hiệu quả NẾU BẠN KHÔNG QUAN TÂM VỀ ĐƠN HÀNG CÓ THỂ CÓ TRONG ĐẦU RA PPRINT , có thể là một vấn đề lớn nếu:

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

Tôi thực sự ngạc nhiên rằng thứ tự không được giữ nguyên ... à tốt.


Một từ điển python được triển khai bằng cách sử dụng một bản đồ băm. Do đó, khi bạn chuyển đổi một OrderDict (sự kết hợp của một chính tả cơ bản và một danh sách để duy trì thứ tự) thành một chính tả, bạn sẽ mất mọi thông tin về đơn đặt hàng. Hơn nữa, phương thức repr được cho là trả về một chuỗi đại diện cho đối tượng trong mã python. Nói cách khác, obj == eval (repr (obj)) hoặc, ở mức tối thiểu là repr (obj) == repr (eval (repr (obj))). Đại diện của OrderedDict làm điều này tốt. dict .__ repr__ cung cấp cho bạn một biểu diễn rất dễ đọc của con người hoàn toàn là một tác dụng phụ của ký tự dict ('{' và '}', v.v.). OrderDict không có cái này.
Marr75,

1

Bạn cũng có thể sử dụng đơn giản hóa câu trả lời kzh này :

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

Nó giữ nguyên thứ tự và sẽ xuất ra gần giống với câu trả lời webwurst ( in qua json dump ).


1

Đối với python <3,8 (ví dụ: 3,6):

Khỉ vá pprint's sortedđể ngăn nó phân loại. Điều này cũng sẽ có lợi cho mọi thứ hoạt động đệ quy và phù hợp hơn jsontùy chọn cho bất kỳ ai cần sử dụng, ví dụ: widththam số:

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

Chỉnh sửa: dọn dẹp

Để làm sạch sau khi kinh doanh bẩn thỉu này, chỉ cần chạy: pprint.sorted = sorted

Đối với một giải pháp thực sự sạch, thậm chí có thể sử dụng trình quản lý ngữ cảnh:

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

Bạn có thể xác định lại pprint()và chặn các cuộc gọi cho OrderedDict's. Đây là một minh họa đơn giản. Theo văn bản, các OrderedDictđang đè bỏ qua bất kỳ tùy chọn stream, indent, width, hoặc depthtừ khóa mà có thể đã được thông qua, nhưng có thể được tăng cường để thực hiện chúng. Đáng tiếc là kỹ thuật này không xử lý bên trong thùng chứa khác, chẳng hạn như một listcủa 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

Nếu tất cả các mục từ điển đều thuộc một loại, bạn có thể sử dụng thư viện xử lý dữ liệu tuyệt vời pandas:

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

hoặc là

>>> import pandas as pd
>>> x = {'foo':'bar', 'baz':'bam'}
>>> pd.Series(x)
baz    bam
foo    bar
dtype: object

2
Mã đó sẽ làm gì? Nó có ưu điểm gì so với các giải pháp khác ở đây?
Elias Zamaria
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.