漂亮的打印数据类更漂亮

max*_*zig 4 python pretty-print python-dataclasses

Python数据类实例还包括一个字符串表示方法,但是当类具有多个字段和/或更长的字段值时,它的结果对于漂亮的打印目的来说并不足够。

基本上,我正在寻找一种方法来自定义默认的数据类字符串表示例程,或者寻找一种理解数据类并更漂亮地打印它们的漂亮打印机。

所以,这只是我想到的一个小自定义:在每个字段之后添加一个换行符,同时在第一个字段之后缩进行。

例如,代替

x = InventoryItem('foo', 23)
print(x) # =>
InventoryItem(name='foo', unit_price=23, quantity_on_hand=0)
Run Code Online (Sandbox Code Playgroud)

我想得到这样的字符串表示:

x = InventoryItem('foo', 23)
print(x) # =>
InventoryItem(
    name='foo',
    unit_price=23,
    quantity_on_hand=0
)
Run Code Online (Sandbox Code Playgroud)

或者类似的。也许漂亮的打印机可以变得更漂亮,例如对齐=分配字符或类似的东西。

当然,它也应该以递归方式工作,例如,也是数据类的字段应该缩进更多。

Drd*_*yor 9

Python 3.10+支持漂亮的打印数据类:

Python 3.10.0b2+ (heads/3.10:f807a4fad4, Sep  4 2021, 18:58:04) [GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dataclasses import dataclass
>>> @dataclass
... class Literal:
...     value: 'Any'
... 
>>> @dataclass
... class Binary:
...     left: 'Binary | Literal'
...     operator: str
...     right: 'Binary | Literal'
... 
>>> from pprint import pprint
>>> # magic happens here
>>> pprint(
... Binary(Binary(Literal(2), '*', Literal(100)), '+', Literal(50)))
Binary(left=Binary(left=Literal(value=2),
                   operator='*',
                   right=Literal(value=100)),
       operator='+',
       right=Literal(value=50))
Run Code Online (Sandbox Code Playgroud)


sim*_*lmx 6

我所关心的只是将字段放在不同的行上,所以我最终使用dataclasses.asdictpprint.pprint

from dataclasses import dataclass, asdict
from pprint import pprint

@dataclass
class SomeClass:
   there: int
   needs: int
   to: int
   be: int
   many: int
   fields: int
   for_: int
   it: int
   to2: int
   work: int


a = SomeClass(there=1, needs=2, to=3, be=4, many=5, fields=6, for_=7, it=8, to2=9, work=10)

pprint(asdict(a), sort_dicts=False)
Run Code Online (Sandbox Code Playgroud)

输出:

{'there': 1,
 'needs': 2,
 'to': 3,
 'be': 4,
 'many': 5,
 'fields': 6,
 'for_': 7,
 'it': 8,
 'to2': 9,
 'work': 10}
Run Code Online (Sandbox Code Playgroud)

我使用的是Python 3.9。

  • 就是这样! (3认同)

sal*_*die 5

我们可以使用dataclasses.fields递归嵌套数据类并漂亮地打印它们:

from collections.abc import Mapping, Iterable
from dataclasses import is_dataclass, fields

def pretty_print(obj, indent=4):
    """
    Pretty prints a (possibly deeply-nested) dataclass.
    Each new block will be indented by `indent` spaces (default is 4).
    """
    print(stringify(obj, indent))

def stringify(obj, indent=4, _indents=0):
    if isinstance(obj, str):
        return f"'{obj}'"

    if not is_dataclass(obj) and not isinstance(obj, (Mapping, Iterable)):
        return str(obj)

    this_indent = indent * _indents * ' '
    next_indent = indent * (_indents + 1) * ' '
    start, end = f'{type(obj).__name__}(', ')'  # dicts, lists, and tuples will re-assign this

    if is_dataclass(obj):
        body = '\n'.join(
            f'{next_indent}{field.name}='
            f'{stringify(getattr(obj, field.name), indent, _indents + 1)},' for field in fields(obj)
        )

    elif isinstance(obj, Mapping):
        if isinstance(obj, dict):
            start, end = '{}'

        body = '\n'.join(
            f'{next_indent}{stringify(key, indent, _indents + 1)}: '
            f'{stringify(value, indent, _indents + 1)},' for key, value in obj.items()
        )

    else:  # is Iterable
        if isinstance(obj, list):
            start, end = '[]'
        elif isinstance(obj, tuple):
            start = '('

        body = '\n'.join(
            f'{next_indent}{stringify(item, indent, _indents + 1)},' for item in obj
        )

    return f'{start}\n{body}\n{this_indent}{end}'

Run Code Online (Sandbox Code Playgroud)

我们可以使用嵌套数据类来测试它:

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

@dataclass
class Coords:
    my_points: list
    my_dict: dict

coords = Coords([Point(1, 2), Point(3, 4)], {'a': (1, 2), (1, 2): 'a'})

pretty_print(coords)

# Coords(
#     my_points=[
#         Point(
#             x=1,
#             y=2,
#         ),
#         Point(
#             x=3,
#             y=4,
#         ),
#     ],
#     my_dict={
#         'a': (
#             1,
#             2,
#         ),
#         (
#             1,
#             2,
#         ): 'a',
#     },
# )
Run Code Online (Sandbox Code Playgroud)

这应该足够通用以涵盖大多数情况。希望这可以帮助!


max*_*zig 5

截至 2021 年(Python 3.9),Python 的标准pprint 还不支持数据类。

然而,prettyprinter包支持数据类并提供一些漂亮的打印功能。

例子:

[ins] In [1]: from dataclasses import dataclass
         ...:
         ...: @dataclass
         ...: class Point:
         ...:     x: int
         ...:     y: int
         ...:
         ...: @dataclass
         ...: class Coords:
         ...:     my_points: list
         ...:     my_dict: dict
         ...:
         ...: coords = Coords([Point(1, 2), Point(3, 4)], {'a': (1, 2), (1, 2): 'a'})

[nav] In [2]: import prettyprinter as pp

[ins] In [3]: pp.pprint(coords)
Coords(my_points=[Point(x=1, y=2), Point(x=3, y=4)], my_dict={'a': (1, 2), (1, 2): 'a'})
Run Code Online (Sandbox Code Playgroud)

默认情况下,未启用数据类支持,因此:

[nav] In [4]: pp.install_extras()
[ins] In [5]: pp.pprint(coords)
Coords(
    my_points=[Point(x=1, y=2), Point(x=3, y=4)],
    my_dict={'a': (1, 2), (1, 2): 'a'}
)
Run Code Online (Sandbox Code Playgroud)

或者强制缩进所有字段:

[ins] In [6]: pp.pprint(coords, width=1)
Coords(
    my_points=[
        Point(
            x=1,
            y=2
        ),
        Point(
            x=3,
            y=4
        )
    ],
    my_dict={
        'a': (
            1,
            2
        ),
        (
            1,
            2
        ): 'a'
    }
)

Run Code Online (Sandbox Code Playgroud)

Prettyprinter 甚至可以语法高亮!(参见cpprint()


注意事项:

  • Prettyprinter 不是 python 标准库的一部分
  • 根本不打印默认值,截至 2021 年,没有办法解决这个问题