Trigger f-string parse on python string in variable

喜夏-厌秋 提交于 2021-01-29 09:11:01

问题


This question comes from handling jupyter magics, but can be expressed in a more simple way. Given a string s = "the key is {d['key']}" and a dictionary d = {'key': 'val'}, we want to parse the string.

The old method would be .format(), which will raise an error - it doesn't handle dictionary keys.

"the key is {d['key']}".format(d=d)  # ERROR

I thought the only way around was to transform the dictionary to an object (explained here or here).

"the key is {d.key}".format(obj(d))

But Martijn explained nicely that you can simply leave out the quotes to get it working:

"the key is {d[key]}".format(d=d)

Still the new method f'string' does handle dictionary keys ain an intuitive python manner:

f"the key is {d['key']}"

It also handles functions - something .format also cannot handle.

f"this means {d['key'].lower()}"

Although we now know that you can do it with .format, I am still wondering about the original question: given s and d, how do you force a f'string' parse of s? I added another example with a function inside the curly brackets, that .format can also not handle and f'string' would be able to solve.

Is there some function .fstring() or method available? What does Python use internally?


回答1:


String formatting can handle most string dictionary keys just fine, but you need to remove the quotes:

"the key is {d[key]}".format(d=d)

Demo:

>>> d = {'key': 'val'}
>>> "the key is {d[key]}".format(d=d)
'the key is val'

str.format() syntax isn't quite the same thing as Python expression syntax (which is what f-strings mostly support).

From the Format String Syntax documentation:

field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
[...]
element_index     ::=  digit+ | index_string
index_string      ::=  <any source character except "]"> +

and

[A]n expression of the form '[index]' does an index lookup using __getitem__()

The syntax is limited, in that it will convert any digit-only strings into an integer, and everything else is always interpreted as a string (though you could use nested {} placeholders to dynamically interpolate a key value from another variable).

If you must support arbitrary expressions, the same way that f-strings do and you do not take template strings from untrusted sources (this part is important), then you could parse out the field name components and then use the eval() function to evaluate the values before you then output the final string:

from string import Formatter

_conversions = {'a': ascii, 'r': repr, 's': str}

def evaluate_template_expressions(template, globals_=None):
    if globals_ is None:
        globals_ = globals()
    result = []
    parts = Formatter().parse(template)
    for literal_text, field_name, format_spec, conversion in parts:
        if literal_text:
            result.append(literal_text)
        if not field_name:
            continue
        value = eval(field_name, globals_)
        if conversion:
            value = _conversions[conversion](value)
        if format_spec:
            value = format(value, format_spec)
        result.append(value)
    return ''.join(result)

Now the quotes are accepted:

>>> s = "the key is {d['key']}"
>>> d = {'key': 'val'}
>>> evaluate_template_expressions(s)
'the key is val'

Essentially, you can do the same with eval(f'f{s!r}', globals()), but the above might give you some more control over what expressions you might want to support.




回答2:


[G]iven s and d, how do you force a f'string' parse of s? Is there some function or method available?

This can be done... using eval. But beware eval!

>>> eval('f' + repr(s))
the key is val

The repr is there to escape any quotes and to wrap s itself with quotes.

If you are aware of which variables to format (d in this case), opt for Martijn's answer of doing str.format. The above solution should be your last resort due to the dangers of eval.



来源:https://stackoverflow.com/questions/55457543/trigger-f-string-parse-on-python-string-in-variable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!