Pretty print namedtuple

穿精又带淫゛_ 提交于 2019-12-04 22:25:29

I use the builtin function vars to get the namedtuple as a dictionary.

However, it returns an OrderedDict which pprint won't indent, so I convert it to a dict:

>>> from collections import namedtuple

>>> Busbar = namedtuple('Busbar', 'id name voltage')
>>> busbar = Busbar(id=102, name='FACTORY', voltage=21.8)

With pprint and dict:

>>> from pprint import pprint
>>> pprint(dict(vars(busbar)))
{'id': 102,
 'name': 'FACTORY',
 'voltage': 21.8}

The pprint PrettyPrinter in Python 3 is much more extendable than it used to be in Python 2. You could create your own printer like below to add methods for the object you want to handle without messing too much with pprint "private" methods and attributes.

You can see an online example here: https://repl.it/HkDd/1

from io import StringIO
import pprint

class MyPrettyPrinter(pprint.PrettyPrinter):
    def format_namedtuple(self, object, stream, indent, allowance, context, level):
        # Code almost equal to _format_dict, see pprint code
        write = stream.write
        write(object.__class__.__name__ + '(')
        object_dict = object._asdict()
        length = len(object_dict)
        if length:
            # We first try to print inline, and if it is too large then we print it on multiple lines
            inline_stream = StringIO()
            self.format_namedtuple_items(object_dict.items(), inline_stream, indent, allowance + 1, context, level, inline=True)
            max_width = self._width - indent - allowance
            if len(inline_stream.getvalue()) > max_width:
                self.format_namedtuple_items(object_dict.items(), stream, indent, allowance + 1, context, level, inline=False)
            else:
                stream.write(inline_stream.getvalue())
        write(')')

    def format_namedtuple_items(self, items, stream, indent, allowance, context, level, inline=False):
        # Code almost equal to _format_dict_items, see pprint code
        indent += self._indent_per_level
        write = stream.write
        last_index = len(items) - 1
        if inline:
            delimnl = ', '
        else:
            delimnl = ',\n' + ' ' * indent
            write('\n' + ' ' * indent)
        for i, (key, ent) in enumerate(items):
            last = i == last_index
            write(key + '=')
            self._format(ent, stream, indent + len(key) + 2,
                         allowance if last else 1,
                         context, level)
            if not last:
                write(delimnl)

    def _format(self, object, stream, indent, allowance, context, level):
        # We dynamically add the types of our namedtuple and namedtuple like 
        # classes to the _dispatch object of pprint that maps classes to
        # formatting methods
        # We use a simple criteria (_asdict method) that allows us to use the
        # same formatting on other classes but a more precise one is possible
        if hasattr(object, '_asdict') and type(object).__repr__ not in self._dispatch:
            self._dispatch[type(object).__repr__] = MyPrettyPrinter.format_namedtuple
        super()._format(object, stream, indent, allowance, context, level)

and use it like so:

from collections import namedtuple

Segment = namedtuple('Segment', 'p1 p2')
# Your own namedtuple-like class
class Node:
    def __init__(self, x, y, segments=[]):
        self.x = x
        self.y = y
        self.segments = segments

    def _asdict(self):
        return {"x": self.x, "y": self.y, "segments": self.segments}

    # Default repr
    def __repr__(self):
        return "Node(x={}, y={}, segments={})".format(self.x, self.y, self.segments)

# A circular structure for the demo
node = Node(0, 0)
segments = [
    Segment(node, Node(1, 1)),
    Segment(node, Node(2, 1)),
    Segment(node, Node(1, 2, segments=[
      Segment(Node(2, 3), Node(1, 1)),
    ])),
]
node.segments = segments

pp = MyPrettyPrinter(indent=2, depth=2)
pp.pprint(node)

outputs

Node(
  x=0,
  y=0,
  segments=[ Segment(
                p1=<Recursion on Node with id=139778851454536>,
                p2=Node(x=1, y=1, segments=[])),
              Segment(
                p1=<Recursion on Node with id=139778851454536>,
                p2=Node(x=2, y=1, segments=[])),
              Segment(
                p1=<Recursion on Node with id=139778851454536>,
                p2=Node(x=1, y=2, segments=[...]))])
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!