漂亮打印命名元组

33

我尝试使用pprint库,但它的输出仅有一行,没有多行输出和缩进。


4
你能举一个你想要打印的对象的例子,以及你希望打印出来的输出是什么样子吗? - TigerhawkT3
1
你期望什么?如果你需要更多控制它的输出,可以创建一个自定义对象并定义 __repr__ - jonrsharpe
3个回答

25

我使用namedtuple的_asdict方法。

但它返回一个OrderedDict,而pprint不会缩进,所以我将其转换为一个dict

>>> from collections import namedtuple

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

使用 pprintdict

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

1
@KFL,不行的。你需要编写一个小实用程序函数。 - Peter Wood
1
从Python 3.7到3.10版本,将vars应用于命名元组会引发TypeError: vars() argument must have __dict__ attribute错误。使用方法_as_dict的早期版本可以解决此问题。 - 0 _
1
@IoannisFilippidis 谢谢,我已经回滚到了之前使用 _as_dict 的答案版本。 - Peter Wood
只是要注意,现在我正在使用dataclassdataclasses.asdict - undefined

17

Python 3中的pprint PrettyPrinter比Python 2中使用的要更加可扩展。您可以创建自己的打印机,如下所示,以添加处理所需对象的方法,而无需过多地干扰pprint“私有”方法和属性。

您可以在此处查看在线示例: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)

然后像这样使用它:

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)

输出

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=[...]))])

4
与其他所有解决方案不同,此解决方案是通用的,可以处理嵌套在其他容器中的命名元组:
import black

# value_to_print can either be a namedtuple, or a container containing tuples,
# or a namedtuple containing containers containing other namedtuples,
# or whatever else you want.
print(black.format_str(repr(value_to_print), mode=black.Mode()))

这需要安装black,可以通过 pip install black 完成。


我不建议使用 sudo 安装 Python 包 - 这可能会导致系统包出现问题并破坏任何虚拟环境。只需使用 pip install.. - 在很少的情况下(我想说没有,但可能有一些特殊情况),您需要使用 sudo pip install.. - Jan Spurny
@JanSpurny - 谢谢,我已经移除了sudo。 - tohava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接