【问题标题】:Python Safe string templates for nested dict object用于嵌套 dict 对象的 Python 安全字符串模板
【发布时间】:2013-05-09 07:37:54
【问题描述】:

我有一个复杂的嵌套字典对象,例如:

value = { 
    'a': '100', 
    bits: {
        1: 'alpha', 
        2: 'beta', 
        3: ['31', '32', 901]
    }
}

我需要使用模板“安全地”格式化它。这意味着如果找不到密钥,只需默默地忽略 {} 占位符。密钥可能不存在,我不想引发 KeyErrors。问题是 string.Template 无法处理与 str.format 相同的功能。我使用的 str.format 类似于:

"a=${a}, b1={bits[1]}, b31={bits[3]}, b9={bits[9]}".format(**value)

输出应该是:

"a=100, b1=alpha, b31=(31, 32, 901), b9="

我不需要花哨的循环或 if/else 条件。只是带有子字典的简单格式。

我有哪些选择?我更喜欢尽可能使用内置函数或非常小的库。

这不是一个网络应用程序,所以如果可能的话,我想避免为此加载像 jinja2 这样的库。

【问题讨论】:

  • 尽管我喜欢重新发明更好的轮子,但我有时间限制并且更喜欢使用已经测试过的东西:-)

标签: python templates string-formatting


【解决方案1】:

编写你自己的格式化程序:

In [1]: from string import Formatter

In [2]: value = { 
   ...:     'a': '100', 
   ...:     'bits': {
   ...:         1: 'alpha', 
   ...:         2: 'beta', 
   ...:         3: ['31', '32', 901]}}

In [3]: class YourFormatter(Formatter):
   ...:     def get_value(self, field_name, args, kwargs):
   ...:         return kwargs.get(field_name, '')
   ...: 
   ...:     def get_field(self, field_name, args, kwargs):
   ...:         first, rest = field_name._formatter_field_name_split() 
   ...:         obj = self.get_value(first, args, kwargs) 
   ...:         
   ...:         for is_attr, i in rest:
   ...:             if is_attr:
   ...:                 obj = getattr(obj, i)
   ...:             else:
   ...:                 obj = obj.get(i, '')
   ...:         return obj, first
   ...:     


In [4]: fmt = YourFormatter()

In [5]: fmt.format("a={a}, b1={bits[1]}, b31={bits[3]}, b9={bits[9]}", **value)
Out[5]: "a=100, b1=alpha, b31=['31', '32', 901], b9="

对于 Python 3,您需要添加

import _string

并替换该行

first, rest = field_name._formatter_field_name_split() 

first, rest = _string.formatter_field_name_split(field_name) 

【讨论】:

  • 完美。正是我需要的!我正要自己写,但你的解决方案要好得多。
  • @Ayman -- 如果有帮助,我很高兴。另请注意,此实现不能普遍使用,因为它不处理 args 例如...
  • 这是我在使用 Python 3.5 AttributeError 尝试此操作时得到的结果:“str”对象没有属性“_formatter_field_name_split”。有什么解决办法吗?
  • 我现在一直在看这个。更改在编辑后的解决方案中。
  • 您能否更改代码以也能够处理bits[3][2] 以返回901?我想要解析的嵌套数据如下所示:{'id': '1', 'values': {'account': [{'text': '1200 Inventory', 'value': '115'}]}}
【解决方案2】:

这样做的唯一方法是编写一个实现 dict 和 sequence 协议的包装类,将任何列表或 dict 返回值包装在同一个类中,并捕获任何 KeyError 或 IndexError 异常。

然后你的电话变成"…".format(**DefaultingWrapper(value))

【讨论】:

  • 感谢您的建议。我尝试使用默认的 dict 实现,但由于嵌套的 dict 不是这个新的默认 dict 类的子类而失败了。换句话说,这只适用于最顶层。因此,实现必须在模板级别完成。
猜你喜欢
  • 1970-01-01
  • 2021-09-25
  • 2012-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多