如何最好地实现自定义漂亮打印机。

26

自定义 pprint.PrettyPrinter

pprint 模块的文档提到,PrettyPrinter.format 方法旨在使自定义格式化成为可能。

我发现可以在子类中重写此方法,但似乎无法让基类方法应用换行和缩进。

  • 我有什么遗漏的吗?
  • 是否有更好的方法来做到这一点(例如另一个模块)?

替代方案?

我查看了pretty 模块,它看起来很有趣,但似乎没有提供一种在不修改其他模块的情况下自定义其他模块中的类格式化的方法。

我想我正在寻找的是一种允许我提供类型(或函数)到处理节点的程序的映射的东西。 处理节点的程序将接受一个节点并返回其字符串表示形式以及子节点列表。 以此类推。

为什么我研究漂亮打印

我的最终目标是紧凑地打印 DocBook 格式的 xml.etree.ElementTree 的自定义格式化部分。

(我惊讶地发现 Python 对 DocBook 的支持不多。也许我在这方面错过了什么。)

我在一个名为xmlearn 的客户端中构建了一些基本功能,该客户端使用lxml。例如,要转储 Docbook 文件,可以执行以下操作:

xmlearn -i docbook_file.xml dump -f docbook -r book

虽然它有点粗糙,但它给了我想要的信息。

xmlearn 还有其他功能,比如构建图像和显示 XML 文档中标签之间关系的转储。这些与此问题几乎没有关系。

您还可以执行任意深度的转储,或指定 XPath 作为起始点集合。XPath 部分有点过时了,因此 docbook 特定格式并不是很成熟。

这仍然不是这个问题的真正答案。我仍然希望能在某个地方找到可定制的漂亮打印机。

4个回答

5

我的解决方案是用一个简单的包装器替换pprint.PrettyPrinter,在调用原始打印机之前对找到的任何浮点数进行格式化。

from __future__ import division
import pprint
if not hasattr(pprint,'old_printer'):
    pprint.old_printer=pprint.PrettyPrinter

class MyPrettyPrinter(pprint.old_printer):
    def _format(self,obj,*args,**kwargs):
        if isinstance(obj,float):
            obj=round(obj,4)
        return pprint.old_printer._format(self,obj,*args,**kwargs)
pprint.PrettyPrinter=MyPrettyPrinter

def pp(obj):
    pprint.pprint(obj)

if __name__=='__main__':
    x=[1,2,4,6,457,3,8,3,4]
    x=[_/17 for _ in x]
    pp(x)

3

这个问题可能是一个重复的问题:


使用pprint.PrettyPrinter

我查看了pprint的源代码,它似乎建议为了增强pprint(),你需要:

  • 子类化PrettyPrinter
  • 覆盖_format()
  • 测试issubclass(),
  • 如果它不是你的类,则传回_format()

替代方案

我认为更好的方法是拥有自己的pprint(),当它不知道如何处理时,它会转而使用pprint.pformat

例如:

'''Extending pprint'''

from pprint import pformat

class CrazyClass: pass

def prettyformat(obj):
    if isinstance(obj, CrazyClass):
        return "^CrazyFoSho^"
    else:
        return pformat(obj)

def prettyp(obj):
    print(prettyformat(obj))

# test
prettyp([1]*100)
prettyp(CrazyClass())

这里的好处是您不依赖于 pprint 的内部机制。它明确而简洁。
缺点是您需要手动处理缩进。

3
Alternative 的另一个缺点是 prettyp([CrazyClass()]) 无法正常工作。 - mic_e
1
请查看此补丁,它已经在相当长一段时间内应用于Python:http://bugs.python.org/issue1373762。这允许您对PrettyPrinter进行子类化并覆盖“format”方法。文本中提供了一个示例。请注意,该示例存在缺陷,因为`True`和`False`将被打印为`0x1`和`0x0`,这隐藏了它们是布尔值的事实。 - mic_e

2
如果您想修改默认的漂亮打印机而不用子类化,可以使用pprint.PrettyPrinter类上的内部_dispatch表。您可以在源代码中看到如何为诸如字典和列表之类的内部类型添加调度的示例。
以下是我如何为MatchPy的Operation类型添加自定义漂亮打印机的方式:
import pprint
import matchpy

def _pprint_operation(self, object, stream, indent, allowance, context, level):
    """
    Modified from pprint dict https://github.com/python/cpython/blob/3.7/Lib/pprint.py#L194
    """
    operands = object.operands
    if not operands:
        stream.write(repr(object))
        return
    cls = object.__class__
    stream.write(cls.__name__ + "(")
    self._format_items(
        operands, stream, indent + len(cls.__name__), allowance + 1, context, level
    )
    stream.write(")")


pprint.PrettyPrinter._dispatch[matchpy.Operation.__repr__] = _pprint_operation

现在,如果我对任何具有与matchpy.Operation相同的__repr__方法的对象使用pprint.pprint,它将使用此方法来漂亮地打印它。这也适用于子类,只要它们不覆盖__repr__方法,这是有意义的!如果你有相同的__repr__方法,你就会有相同的漂亮打印行为。
下面是一些MatchPy操作的漂亮打印示例:
ReshapeVector(Vector(Scalar('1')),
              Vector(Index(Vector(Scalar('0')),
                           If(Scalar('True'),
                              Scalar("ReshapeVector(Vector(Scalar('2'), Scalar('2')), Iota(Scalar('10')))"),
                              Scalar("ReshapeVector(Vector(Scalar('2'), Scalar('2')), Ravel(Iota(Scalar('10'))))")))))

0

1
一个支持Python 3.x的改进和维护版本可以在IPython(作为IPython.lib.pretty)中找到:http://ipython.org/ipython-doc/stable/api/generated/IPython.lib.pretty.html - Mikhail Korobov
此答案仅包含链接,请展开回答并说明模块的使用方法。 - user202729

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