2

我使用许多类似 json 的字典。pprint对于构建它们很方便。有没有办法使 pprint 输出中的所有整数都以十六进制而不是十进制打印?

例如,而不是:

{66: 'far',
 99: 'Bottles of the beer on the wall',
 '12': 4277009102,
 'boo': 21,
 'pprint': [16, 32, 48, 64, 80, 96, 112, 128]}

我宁愿看到:

{0x42: 'far',
 0x63: 'Bottles of the beer on the wall',
 '12': 0xFEEDFACE,
 'boo': 0x15,
 'pprint': [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]}

我曾尝试自定义PrettyPrinter,但无济于事,我是否能够导致上述情况,PrettyPrinter.format()处理整数似乎仅适用于某些整数:

class MyPrettyPrinter(PrettyPrinter):
    def format(self, object, context, maxlevels, level):
        if isinstance(object, int):
            return '0x{:X}'.format(object), True, False
        return super().format(object, context, maxlevels, level)

上面的类产生

{0x42: 'far',
 0x63: 'Bottles of the beer on the wall',
 '12': 0xFEEDFACE,
 'boo': 0x15,
 'pprint': [16, 32, 48, 64, 80, 96, 112, 128]}

列表内容的格式不正确。

4

2 回答 2

6

可以更改 的输出pprint,但您需要重新实现该saferepr()函数,而不仅仅是子类化pprint.PrettyPrinter()该类。

发生的情况是该函数(的内部版本)saferepr()用于所有对象,然后该函数本身递归地处理将对象转换为表示形式(仅使用其自身,而不是PrettyPrinter()实例),因此必须那里进行任何自定义。只有当结果saferepr()变得太大(对于配置的宽度来说太宽)时,PrettyPrinter类才会开始将容器输出分解为组件以放在单独的行上;saferepr()然后对组件元素重复调用过程。

所以PrettyPrinter.format()只负责处理顶层对象,并且每个递归对象是a)在一个受支持的容器类型(dict、list、tuple、string和这些的标准库子类)和b)其中表示的父级产生的容器.format()超出了显示宽度。

为了能够覆盖实现,我们需要了解.format()方法和saferepr()实现如何交互,它们采用什么参数以及它们需要返回什么。

PrettyPrinter.format()传递了额外的参数contextmaxlevelslevel

  • context用于检测递归(默认实现返回_recursion(object)ifid(object) in context为 true 的结果。
  • maxlevels设置level >= maxlevels为真时,默认实现...作为容器的内容返回。

该方法还应该返回 3 个值的元组;表示字符串和两个标志。您可以放心地忽略这些标志的含义,它们实际上从未在当前实现中使用过。它们旨在表明生成的表示是“可读的”(使用可以传递给的 Python 语法eval())还是递归的(对象包含循环引用)。但是 PrettyPrinter.isreadable()andPrettyPrinter.isrecursive()方法实际上完全绕过了.format();这些返回值似乎是重构破坏了.format()这两种方法之间的关系的保留。因此,只需返回一个表示字符串和您喜欢的任何两个布尔值。

.format()真的只是委托给它的内部实现saferepr()然后做几件事

  • 处理递归检测context, 和深度处理maxlevelslevel
  • 递归字典、列表和元组(以及它们的子类,只要它们的__repr__方法仍然是默认实现)
  • 对于字典,对键值对进行排序。这比它在 Python 3 中出现的要棘手,但这可以通过一个自定义_safe_tuple排序键来解决,该键近似于 Python 2 的排序所有行为。我们可以重复使用它。

为了实现递归替换,我更喜欢使用@functools.singledispatch()委托处理不同的类型。忽略自定义__repr__方法、处理深度问题、递归和空对象,也可以由装饰器处理:

import pprint
from pprint import PrettyPrinter
from functools import singledispatch, wraps
from typing import get_type_hints

def common_container_checks(f):
    type_ = get_type_hints(f)['object']
    base_impl = type_.__repr__
    empty_repr = repr(type_())   # {}, [], ()
    too_deep_repr = f'{empty_repr[0]}...{empty_repr[-1]}'  # {...}, [...], (...)
    @wraps(f)
    def wrapper(object, context, maxlevels, level):
        if type(object).__repr__ is not base_impl:  # subclassed repr
            return repr(object)
        if not object:                              # empty, short-circuit
            return empty_repr
        if maxlevels and level >= maxlevels:        # exceeding the max depth
            return too_deep_repr
        oid = id(object)
        if oid in context:                          # self-reference
            return pprint._recursion(object)
        context[oid] = 1
        result = f(object, context, maxlevels, level)
        del context[oid]
        return result
    return wrapper

@singledispatch
def saferepr(object, context, maxlevels, level):
    return repr(object)

@saferepr.register(int)
def _handle_int(object: int, *args):
    # uppercase hexadecimal representation with 0x prefix
    return f'0x{object:X}'

@saferepr.register(dict)
@common_container_checks
def _handle_dict(object: dict, context, maxlevels, level):
    level += 1
    contents = [
        f'{saferepr(k, context, maxlevels, level)}: '
        f'{saferepr(v, context, maxlevels, level)}'
        for k, v in sorted(object.items(), key=pprint._safe_tuple)
    ]
    return f'{{{", ".join(contents)}}}'

@saferepr.register(list)
@common_container_checks
def _handle_list(object: list, context, maxlevels, level):
    level += 1
    contents = [
        f'{saferepr(v, context, maxlevels, level)}'
        for v in object
    ]
    return f'[{", ".join(contents)}]'

@saferepr.register(tuple)
@common_container_checks
def _handle_tuple(object: tuple, context, maxlevels, level):
    level += 1
    if len(object) == 1:
        return f'({saferepr(object[0], context, maxlevels, level)},)'
    contents = [
        f'{saferepr(v, context, maxlevels, level)}'
        for v in object
    ]
    return f'({", ".join(contents)})'

class HexIntPrettyPrinter(PrettyPrinter):
    def format(self, *args):
        # it doesn't matter what the boolean values are here
        return saferepr(*args), True, False

这个handfull可以处理基本pprint实现可以处理的任何事情,并且它将在任何受支持的容器中生成十六进制整数。只需创建一个HexIntPrettyPrinter()类的实例并调用.pprint()它:

>>> sample = {66: 'far',
...  99: 'Bottles of the beer on the wall',
...  '12': 4277009102,
...  'boo': 21,
...  'pprint': [16, 32, 48, 64, 80, 96, 112, 128]}
>>> pprinter = HexIntPrettyPrinter()
>>> pprinter.pprint(sample)
{0x42: 'far',
 0x63: 'Bottles of the beer on the wall',
 '12': 0xFEEDFACE,
 'boo': 0x15,
 'pprint': [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]}

旁注:从 Python 3.7 开始,您可以删除装饰器的(<type>)调用部分;@saferepr.registation(<type>)类型是从注释中提取的。

于 2018-03-29T21:24:18.637 回答
0

更新

我已经在代码中实现了我的概念。这似乎工作得很好。

只需将 pph 用于“pretty print hex”或“ppf”用于“pretty print hex (format)”(返回结果)。

from pprint import PrettyPrinter
pp = PrettyPrinter(indent=4).pprint
pf = PrettyPrinter(indent=4).pformat
def pph(o):
    print(re.sub(r"((?:, +|: +|\( *|\[ *|\{ *)-?)(\d\d+)(?=[,)}\]])", lambda m: m.group(1) + hex(m.group(2)), pf(o)))
def pfh(o):
    return re.sub(r"((?:, +|: +|\( *|\[ *|\{ *)-?)(\d\d+)(?=[,)}\]])", lambda m: m.group(1) + hex(m.group(2)), pf(o))

原帖

哇,听起来真的很复杂。我能问一下这样做有什么问题吗?

d = pprint.pformat(data)
print re.sub(r'(\b\d+)L', lambda x: "0x{:x}".format(int(x.group(1))), d)

它适用于我的数据,这无疑是全部long而不是int(提供方便的L锚点),并且没有引用文字数字的情况 - 但可以轻松处理

re.split(r"('[^']+')", d)

我承认这不是一个很好的解决方案,但考虑到替代方案,至少它也不复杂。

{'funcStartRanges': [],
 'noCodeRanges': [],
 'noOwnerRanges': [{'last': 0x140ce1332, 'length': 0x12, 'start': 0x140ce1321},
                   {'last': 0x140ce1332, 'length': 0x12, 'start': 0x140ce1321}],
 'otherOwnerRanges': [{'last': 0x140ce1332,
                       'length': 0x12,
                       'start': 0x140ce1321}],
 'weOwnItRanges': []}
于 2020-12-14T20:11:38.577 回答