漂亮打印

除了语法高亮之外,Rich 还会格式化(即 *漂亮打印*)容器,例如列表、字典和集合。

运行以下命令查看漂亮打印输出的示例

python -m rich.pretty

注意输出将如何调整以适应终端宽度。

pprint 方法

pprint() 方法提供了一些额外的参数,你可以使用它们来微调对象的漂亮打印方式。以下是导入它的方法

>>> from rich.pretty import pprint
>>> pprint(locals())

缩进指南

Rich 可以绘制 *缩进指南* 来突出显示数据结构的缩进级别。这可以使阅读更深层次的嵌套输出变得更容易。pprint 方法默认情况下启用缩进指南。你可以设置 indent_guides=False 来禁用此功能。

展开所有

Rich 在展开数据结构方面非常保守,并会尝试在每行中尽可能多地容纳内容。如果你愿意,你可以告诉 Rich 通过设置 expand_all=True 来完全展开所有数据结构。以下是一个示例

>>> pprint(["eggs", "ham"], expand_all=True)

截断漂亮输出

非常长的数据结构可能难以阅读,你可能会发现自己要在终端中滚动多个页面才能找到你感兴趣的数据。Rich 可以截断容器和长字符串,以便让你在不淹没终端的情况下获得概览。

如果你将 max_length 参数设置为一个整数,Rich 将截断元素数量超过指定数量的容器。如果数据被截断,Rich 将显示省略号 ... 以及未显示的元素数量。

以下是一个示例

>>> pprint(locals(), max_length=2)

如果你将 max_string 参数设置为一个整数,Rich 将截断超过该长度的字符串。截断的字符串将附加未显示的字符数量。以下是一个示例

>>> pprint("Where there is a Will, there is a Way", max_string=21)

漂亮可渲染对象

Rich 提供了一个 Pretty 类,你可以使用它将漂亮打印的数据插入另一个可渲染对象中。

以下示例在简单面板中显示漂亮打印的数据

from rich import print
from rich.pretty import Pretty
from rich.panel import Panel

pretty = Pretty(locals())
panel = Panel(pretty)
print(panel)

有许多选项可以微调漂亮格式,有关详细信息,请参阅 Pretty 参考。

Rich Repr 协议

Rich 可以语法高亮显示任何输出,但格式化仅限于内置容器、数据类和 Rich 已知的其他对象,例如由 attrs 库生成的那些对象。要为自定义对象添加 Rich 格式化功能,你可以实现 *Rich Repr 协议*。

运行以下命令查看 Rich repr 协议可以生成的内容示例

python -m rich.repr

首先,让我们看一下可能受益于 Rich repr 的类

class Bird:
    def __init__(self, name, eats=None, fly=True, extinct=False):
        self.name = name
        self.eats = list(eats) if eats else []
        self.fly = fly
        self.extinct = extinct

    def __repr__(self):
        return f"Bird({self.name!r}, eats={self.eats!r}, fly={self.fly!r}, extinct={self.extinct!r})"

BIRDS = {
    "gull": Bird("gull", eats=["fish", "chips", "ice cream", "sausage rolls"]),
    "penguin": Bird("penguin", eats=["fish"], fly=False),
    "dodo": Bird("dodo", eats=["fruit"], fly=False, extinct=True)
}
print(BIRDS)

此脚本的结果将是

{'gull': Bird('gull', eats=['fish', 'chips', 'ice cream', 'sausage rolls'], fly=True, extinct=False), 'penguin': Bird('penguin', eats=['fish'], fly=False, extinct=False), 'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)}

输出太长,需要换行,这使得它难以阅读。repr 字符串信息量很大,但有点冗长,因为它们包含默认参数。如果我们使用 Rich 打印它,情况会略有改善

{
    'gull': Bird('gull', eats=['fish', 'chips', 'ice cream', 'sausage rolls'],
fly=True, extinct=False),
    'penguin': Bird('penguin', eats=['fish'], fly=False, extinct=False),
    'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)
}

Rich 知道如何格式化容器字典,但 repr 字符串仍然很冗长,并且输出存在一些换行(假设一个 80 个字符的终端)。

我们可以通过添加以下 __rich_repr__ 方法来解决这两个问题

def __rich_repr__(self):
    yield self.name
    yield "eats", self.eats
    yield "fly", self.fly, True
    yield "extinct", self.extinct, False

现在,如果我们使用 Rich 打印同一个对象,我们将看到以下内容

{
    'gull': Bird(
        'gull',
        eats=['fish', 'chips', 'ice cream', 'sausage rolls']
    ),
    'penguin': Bird('penguin', eats=['fish'], fly=False),
    'dodo': Bird('dodo', eats=['fruit'], fly=False, extinct=True)
}

默认参数已被省略,并且输出已得到很好地格式化。即使我们的终端空间更小,或者我们的对象是深层嵌套数据结构的一部分,输出仍然可读

{
    'gull': Bird(
        'gull',
        eats=[
            'fish',
            'chips',
            'ice cream',
            'sausage rolls'
        ]
    ),
    'penguin': Bird(
        'penguin',
        eats=['fish'],
        fly=False
    ),
    'dodo': Bird(
        'dodo',
        eats=['fruit'],
        fly=False,
        extinct=True
    )
}

你可以为任何类添加 __rich_repr__ 方法来启用 Rich 格式化。此方法应该返回一个元组可迭代对象。你可以返回一个元组列表,但使用 yield 关键字更易于表达,使其成为一个 *生成器*。

每个元组指定输出中的一个元素。

  • yield value 将生成一个位置参数。

  • yield name, value 将生成一个关键字参数。

  • yield name, value, default 将生成一个关键字参数,*如果* value 不等于 default

如果你使用 None 作为 name,那么它也将被视为位置参数,以便支持具有 tuple 位置参数。

你还可以告诉 Rich 生成 *尖括号* 样式的 repr,这种样式通常用于没有简单方法重新创建对象构造函数的情况。为此,请在 __rich_repr__ 方法之后立即将函数属性 "angular" 设置为 True。例如

__rich_repr__.angular = True

这将把 Rich repr 示例的输出更改为以下内容

{
    'gull': <Bird 'gull' eats=['fish', 'chips', 'ice cream', 'sausage rolls']>,
    'penguin': <Bird 'penguin' eats=['fish'] fly=False>,
    'dodo': <Bird 'dodo' eats=['fruit'] fly=False extinct=True>
}

请注意,你可以为第三方库添加 __rich_repr__ 方法,*而无需* 将 Rich 作为依赖项包含在内。如果未安装 Rich,那么将不会出现任何问题。希望将来会有更多第三方库采用 Rich repr 方法。

类型提示

如果你想为 Rich repr 方法添加类型提示,你可以导入并返回 rich.repr.Result,这将有助于捕获逻辑错误

import rich.repr


class Bird:
    def __init__(self, name, eats=None, fly=True, extinct=False):
        self.name = name
        self.eats = list(eats) if eats else []
        self.fly = fly
        self.extinct = extinct

    def __rich_repr__(self) -> rich.repr.Result:
        yield self.name
        yield "eats", self.eats
        yield "fly", self.fly, True
        yield "extinct", self.extinct, False

自动 Rich Repr

如果参数名称与你的属性名称相同,Rich 可以自动生成一个 Rich repr。

要自动构建 Rich repr,请使用 auto() 类装饰器。上面的 Bird 示例遵循上述规则,因此我们严格来说不需要实现我们自己的 __rich_repr__。以下代码将生成相同的 repr

import rich.repr

@rich.repr.auto
class Bird:
    def __init__(self, name, eats=None, fly=True, extinct=False):
        self.name = name
        self.eats = list(eats) if eats else []
        self.fly = fly
        self.extinct = extinct


BIRDS = {
    "gull": Bird("gull", eats=["fish", "chips", "ice cream", "sausage rolls"]),
    "penguin": Bird("penguin", eats=["fish"], fly=False),
    "dodo": Bird("dodo", eats=["fruit"], fly=False, extinct=True)
}
from rich import print
print(BIRDS)

请注意,装饰器还将创建一个 __repr__,因此即使你没有使用 Rich 打印,你也将获得一个自动生成的 repr。

如果你想自动生成角括号类型 repr,那么请在装饰器上设置 angular=True

@rich.repr.auto(angular=True)
class Bird:
    def __init__(self, name, eats=None, fly=True, extinct=False):
        self.name = name
        self.eats = list(eats) if eats else []
        self.fly = fly
        self.extinct = extinct

示例

请参阅 repr.py 以获取此页面中使用的示例代码。