简体   繁体   中英

Python Safe string templates for nested dict object

I have a complex nested dict object, eg:

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

I need to 'safely' format it using a template. Meaning if the keys are not found, just silently ignore the {} place-holders. The Keys might not exist, and I do not want to raise KeyErrors. The problem is that string.Template cannot handle the same functionality that str.format does. The str.format I used is something like:

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

and the output should be:

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

I do not need fancy loops or if/else conditions. Just simple formats with sub dicts.

What are the options I have? I prefer to use built-ins as much as possible or a very small library.

This is not a web app, so no if possible I want to avoid loading a lib like jinja2 just for this.

Write you own formatter:

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="

for Python 3, you need to add

import _string

and replace the line

first, rest = field_name._formatter_field_name_split() 

with

first, rest = _string.formatter_field_name_split(field_name) 

The only way to do this is to write a wrapper class that implements the dict and sequence protocols, wrapping any list or dict return values in the same class, and catch any KeyError or IndexError exceptions.

Then your call becomes "…".format(**DefaultingWrapper(value)) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM