如何让 Python 优雅地格式化 None 和不存在的字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20248355/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 19:59:59  来源:igfitidea点击:

How to get Python to gracefully format None and non-existing fields

pythonstring-formattingmissing-data

提问by Juan A. Navarro

If I write in 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))

I get:

我得到:

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'

Instead of an error message, how can I get Python to more gracefully format the None's and non existent fields?

除了错误消息,我怎样才能让 Python 更优雅地格式化 None 和不存在的字段?

To give an example, I would like to see in the output something more like:

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

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

Ideally, of course, I would like to be able to specify the string used instead of those missing values.

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

采纳答案by dawg

The recommendation in PEP 3101is to subclass Formatter:

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, ~~, !!, ~~

As set up, it will print ~~if a field or attribute is not found and !!if an invalid format is used given the field value. (Just use Nonefor the keyword argument bad_fmtif you want the default of a value error raised.)

在设置时,~~如果未找到字段或属性,并且!!在给定字段值的情况下使用了无效格式,它将打印。(如果您希望引发值错误的默认值,请仅None用于关键字参数bad_fmt。)

To handle missing keys, you need to subclass both get_fieldto catch the KeyErroror AttributeErrorand format_fieldto return a default value for the missing key.

要处理丢失的键,您需要子类化get_field以捕获KeyErrororAttributeErrorformat_field返回丢失键的默认值。

Since you are catching format_fielderrors, you can catch a bad format field as well by catching the ValueErrorfrom the superclass.

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

回答by Martijn Pieters

The str.format()method doesn't give you a direct method to handle missing keys or replace values.

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

You canadd a layer of indirection; pass in a mapping that handles missing and Nonevalues, and alter the format to use just that argument:

可以添加一个间接层; 传入处理缺失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)))

Now all slots refer to positional argument 0, which is treated like a dictionary, but key lookups always succeed and both missing values and Noneare replaced by a placeholder value.

现在所有槽都引用位置参数0,它被视为字典,但键查找总是成功并且两个缺失值None都被占位符值替换。

Here the PlaceholderFormatValue()ensures that regardless of what the format spec gives, the value can be interpolated into the format. This makes {0[k]:.2f}work, for example.

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

By wrapping any dictvalues and having PlaceholderFormatValuehandle item access, the above can also handle failure to provide nested keys or whole dictionaries:

通过包装任何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, ~, ~, ~

回答by Simeon Visser

If you're able to do the formatting separately you could use Template.safe_substitutewhich gracefully handles missing values:

如果您能够单独进行格式化,则可以使用Template.safe_substitutewhich 优雅地处理缺失值:

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