Pretty-print dataclasses prettier

We Are Going To Discuss About Pretty-print dataclasses prettier. So lets Start this Python Article.

Pretty-print dataclasses prettier

  1. How to solve Pretty-print dataclasses prettier

    The pprint package supports pretty printing only since version 3.10 (NB: Python 3.10 was released in 2021).
    Example:
    [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'}) [ins] In [15]: pprint.pprint(coords, width=20) Coords(my_points=[Point(x=1, y=2), Point(x=3, y=4)], my_dict={'a': (1, 2), (1, 2): 'a'})

    When using Python 3.9 or older, there is the prettyprinter package that supports dataclasses and provides some nice pretty-printing features.
    Example:
    [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'})
    The dataclasses support isn't enabled, by default, thus:
    [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'} )
    Or to force indenting of all fields:
    [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' } )
    Prettyprinter can even syntax-highlight! (cf. cpprint())

    Considerations:
    prettyprinter isn't part of the python standard library
    default values aren't printed, at all and as of 2021 there is no way around this
    prettyprinter is pretty-printing very slowly, i.e. much slower than the standard pprint, e.g. for checking if a value is a default value, it's compared against a default-constructed value

  2. Pretty-print dataclasses prettier

    The pprint package supports pretty printing only since version 3.10 (NB: Python 3.10 was released in 2021).
    Example:
    [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'}) [ins] In [15]: pprint.pprint(coords, width=20) Coords(my_points=[Point(x=1, y=2), Point(x=3, y=4)], my_dict={'a': (1, 2), (1, 2): 'a'})

    When using Python 3.9 or older, there is the prettyprinter package that supports dataclasses and provides some nice pretty-printing features.
    Example:
    [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'})
    The dataclasses support isn't enabled, by default, thus:
    [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'} )
    Or to force indenting of all fields:
    [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' } )
    Prettyprinter can even syntax-highlight! (cf. cpprint())

    Considerations:
    prettyprinter isn't part of the python standard library
    default values aren't printed, at all and as of 2021 there is no way around this
    prettyprinter is pretty-printing very slowly, i.e. much slower than the standard pprint, e.g. for checking if a value is a default value, it's compared against a default-constructed value

Solution 1

The pprint package supports pretty printing only since version 3.10 (NB: Python 3.10 was released in 2021).

Example:

[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'})

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

When using Python 3.9 or older, there is the prettyprinter package that supports dataclasses and provides some nice pretty-printing features.

Example:

[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'})

The dataclasses support isn’t enabled, by default, thus:

[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'}
)

Or to force indenting of all fields:

[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'
    }
)

Prettyprinter can even syntax-highlight! (cf. cpprint())


Considerations:

  • prettyprinter isn’t part of the python standard library
  • default values aren’t printed, at all and as of 2021 there is no way around this
  • prettyprinter is pretty-printing very slowly, i.e. much slower than the standard pprint, e.g. for checking if a value is a default value, it’s compared against a default-constructed value

Original Author edited Nov 12, 2021 at 16:11 Of This Content

Solution 2

Python 3.10+ Supports pretty printing dataclasses:

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

Sadly, it is not in wide use (…yet, as of 2021)

Original Author Drdilyor Of This Content

Solution 3

We can use dataclasses.fields to recurse through nested dataclasses and pretty print them:

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

We can test it with a nested dataclass:

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',
#     },
# )

This should be general enough to cover most cases. Hope this helps!

Original Author salt-die Of This Content

Solution 4

You should probably use prettyprinter but if you can’t add dependencies for some reason then you could use this, which is ever so slightly shorter than salt-die’s example (because it uses pprint)

import dataclasses
import pprint


def dcppformat(x, chars=0):
    def parts():
        if dataclasses.is_dataclass(x):
            yield type(x).__name__ + "("

            def fields():
                for field in dataclasses.fields(x):
                    nindent = chars + len(field.name) + 4
                    value = getattr(x, field.name)
                    rep_value = dcppformat(value)
                    yield " " * (chars + 3) + indent_body_chars(
                        "{}={}".format(field.name, rep_value), chars=nindent
                    )

            yield ",\n".join(fields())
            yield " " * chars + ")"
        else:
            yield pprint.pformat(x)

    return "\n".join(parts())


def indent(x, level=1):
    indent_chars(x, level * 4)


def indent_chars(x, chars=1):
    return "\n".join(" " * chars + p for p in x.split("\n"))


def indent_body_chars(x, chars=4):
    a, *b = x.split("\n")
    if b:
        return a + "\n" + indent_chars("\n".join(b), chars=chars,)
    else:
        return a


def dcpprint(x):
    print(dcppformat(x))


def test():
    @dataclasses.dataclass
    class A:
        a: object
        b: str

    dcpprint(A(a=A(a=None, b={"a": 1, "c": 1, "long": "a" * 100}), b=2))


if __name__ == "__main__":
    test()

Original Author Att Righ Of This Content

Conclusion

So This is all About This Tutorial. Hope This Tutorial Helped You. Thank You.

Also Read,

ittutorial team

I am an Information Technology Engineer. I have Completed my MCA And I have 4 Year Plus Experience, I am a web developer with knowledge of multiple back-end platforms Like PHP, Node.js, Python and frontend JavaScript frameworks Like Angular, React, and Vue.

Leave a Comment