漂亮的打印命名元组

Posted

技术标签:

【中文标题】漂亮的打印命名元组【英文标题】:Pretty print namedtuple 【发布时间】:2015-07-15 17:49:26 【问题描述】:

我从pprint尝试了pprint,但是它的输出只有一行,没有多行输出,也没有缩进。

【问题讨论】:

您能否举例说明您要打印的对象以及您希望打印输出的显示方式? 您期待什么?如果您需要更多地控制它的打印方式,请创建一个自定义对象并定义__repr__ 【参考方案1】:

我使用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

【讨论】:

@KFL 这行不通,不。您必须编写一个小实用程序函数。 从 Python 3.7--3.10 开始,将 vars 应用于命名元组会引发 TypeError: vars() argument must have __dict__ attribute。使用 _as_dict 方法的早期版本有效。 @IoannisFilippidis 谢谢,我已经回滚到使用_as_dict 的答案的先前版本。【参考方案2】:

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

【讨论】:

【参考方案3】:

与此处的所有其他解决方案不同,此解决方案是通用的,也适用于其他容器内的命名元组:

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

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

【讨论】:

我不建议使用sudo 来安装python 包——这可能会导致系统包出现问题并破坏任何virtualenvs。只需使用pip install.. - 您想要使用sudo pip install.. 的实例并不多(我会说没有,但可能有一些特殊情况) @JanSpurny - 谢谢,我已经删除了 sudo

以上是关于漂亮的打印命名元组的主要内容,如果未能解决你的问题,请参考以下文章

带有 Qt 漂亮打印机的 gdb

漂亮地打印熊猫数据框

漂亮打印到文件?

如何使用 Go 漂亮地打印 JSON?

Boost 无序地图漂亮打印机正在多次打印值以及不需要的值

新的 jupyter 笔记本中的 sympy 漂亮打印是不是损坏?