Pretty print namedtuple

2019-04-18 14:28发布

I tried pprint from pprint, but its output is just one line, there is no multiline output and no indentation.

2条回答
淡お忘
2楼-- · 2019-04-18 15:06

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 star\"
3楼-- · 2019-04-18 15:09

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=[...]))])
查看更多
登录 后发表回答