如何让Python优雅地格式化None和non-existing字段

2024-04-29 12:07:44 发布

您现在位置:Python中文网/ 问答频道 /正文

如果我用Python编写:

data = {'n': 3, 'k': 3.141594, 'p': {'a': 7, 'b': 8}}
print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
del data['k']
data['p']['b'] = None
print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))

我得到:

3, 3.14, 7, 8
Traceback (most recent call last):
  File "./funky.py", line 186, in <module>
    print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
KeyError: 'k'

如何让Python更优雅地格式化None's和non existent字段,而不是错误消息?

举个例子,我想在输出中看到更像:

3, 3.14, 7, 8
3, ~, 7, ~

当然,理想情况下,我希望能够指定使用的字符串,而不是那些缺少的值。


Tags: inpynoneformatmostdatalinecall
3条回答

PEP 3101中的建议是将Formatter子类化:

import string
class PartialFormatter(string.Formatter):
    def __init__(self, missing='~~', bad_fmt='!!'):
        self.missing, self.bad_fmt=missing, bad_fmt

    def get_field(self, field_name, args, kwargs):
        # Handle a key not found
        try:
            val=super(PartialFormatter, self).get_field(field_name, args, kwargs)
            # Python 3, 'super().get_field(field_name, args, kwargs)' works
        except (KeyError, AttributeError):
            val=None,field_name 
        return val 

    def format_field(self, value, spec):
        # handle an invalid format
        if value==None: return self.missing
        try:
            return super(PartialFormatter, self).format_field(value, spec)
        except ValueError:
            if self.bad_fmt is not None: return self.bad_fmt   
            else: raise

fmt=PartialFormatter()
data = {'n': 3, 'k': 3.141594, 'p': {'a': '7', 'b': 8}}
print(fmt.format('{n}, {k:.2f}, {p[a]}, {p[b]}', **data))
# 3, 3.14, 7, 8
del data['k']
data['p']['b'] = None
print(fmt.format('{n}, {k:.2f}, {p[a]:.2f}, {p[b]}', **data))
# 3, ~~, !!, ~~

按照设置,如果找不到字段或属性,则它将打印~~;如果给定字段值,则使用无效格式,则它将打印!!。(如果希望引发值错误的默认值,只需使用None作为关键字参数bad_fmt。)

要处理丢失的键,您需要对get_field进行子类化,以捕获KeyErrorAttributeErrorformat_field来返回丢失键的默认值。

由于您捕获的是format_field错误,因此也可以通过捕获超类中的ValueError来捕获错误的格式字段。

如果您能够单独进行格式化,那么可以使用Template.safe_substitute来优雅地处理丢失的值:

>>> from string import Template
>>> t = Template("$a $b $c")
>>> t.safe_substitute(a=3)
'3 $b $c'

str.format()方法没有为您提供处理丢失的键或替换值的直接方法。

您可以添加一个间接层;传入一个处理缺失值和None值的映射,并更改格式以仅使用该参数:

class PlaceholderFormatValue():
    def __format__(self, spec):
        return '~'
    def __getitem__(self, name):
        # handle further nested item access
        return self

class formatting_dict(dict):
    def __getitem__(self, name):
        value = self.get(name)
        if isinstance(value, dict):
            # rewrap nested dictionaries to handle missing nested keys
            value = type(self)(value)
        return value if value is not None else PlaceholderFormatValue()

print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))

现在所有的槽都引用位置参数0,它被当作字典对待,但是键查找总是成功的,丢失的值和None都被占位符值替换。

这里PlaceholderFormatValue()确保无论格式规范给出什么,值都可以插入到格式中。例如,这使得{0[k]:.2f}工作。

通过包装任何dict值并具有PlaceholderFormatValue句柄项访问,上述方法还可以处理未能提供嵌套键或整个字典的情况:

>>> data = {'n': 3, 'k': 3.141594, 'p': {'a': 7, 'b': 8}}
>>> del data['k']
>>> data['p']['b'] = None
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, 7, ~
>>> del data['p']['a']
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, ~, ~
>>> del data['p']
>>> print('{0[n]}, {0[k]:.2f}, {0[p][a]}, {0[p][b]}'.format(formatting_dict(data)))
3, ~, ~, ~

相关问题 更多 >