在变量中的python字符串上触发f字符串解析 [英] Trigger f-string parse on python string in variable

查看:37
本文介绍了在变量中的python字符串上触发f字符串解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题来自处理jupyter magics,但可以用更简单的方式表达.给定一个字符串 s = "the key is {d['key']}" 和一个字典 d = {'key': 'val'},我们想要解析字符串.

旧方法是 .format(),它会引发错误 - 它不处理字典键.

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

我认为唯一的方法是将字典转换为对象(解释为 此处或此处).

"key是{d.key}".format(obj(d))

但是 Martijn 很好地解释说,您可以简单地省略引号以使其正常工作:

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

仍然是新方法 f'string' 确实以一种直观的 Python 方式处理字典键:

f"key是{d['key']}"

它还处理函数——某些 .format 也不能处理.

f"这意味着 {d['key'].lower()}"

虽然我们现在知道你可以.format做到这一点,但我仍然想知道最初的问题:给定sd,你如何强制对 s 进行 f'string' 解析?我在大括号内添加了另一个带有函数的示例,.format 也无法处理,而 f'string' 可以解决.

是否有一些函数 .fstring() 或方法可用?Python 内部使用什么?

解决方案

字符串格式化可以处理大部分字符串字典键没问题,但是需要去掉引号:

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

演示:

<预><代码>>>>d = {'key': 'val'}>>>密钥是 {d[key]}".format(d=d)'关键是val'

str.format() 语法与 Python 表达式语法(这是 f-strings 主要支持的)不太一样.

来自格式字符串语法文档一个>:

<块引用>

field_name ::= arg_name ("." attribute_name | "[" element_index "]")*[...]element_index ::= 数字+ |索引字符串index_string ::= <除]"之外的任何源字符>+

<块引用>

[A]n 形式的 '[index]' 表达式使用 __getitem__()

进行索引查找

语法是有限的,因为它会将任何纯数字字符串转换为整数,而其他所有内容始终被解释为字符串(尽管您可以使用嵌套的 {} 占位符来动态插入来自另一个变量的键值).

如果您必须支持任意表达式,就像 f-strings 一样,并且您不要从不受信任的来源获取模板字符串(这部分很重要),然后您可以解析字段名称组件 然后使用 eval()函数在输出最终字符串之前评估值:

from string import Formatter_conversions = {'a':ascii,'r':repr,'s':str}defevaluate_template_expressions(template, globals_=None):如果 globals_ 是 None:globals_ = globals()结果 = []零件 = 格式化程序().解析(模板)对于literal_text、field_name、format_spec、部分转换:如果文字_文本:result.append(literal_text)如果不是 field_name:继续值 = eval(field_name, globals_)如果转换:值 = _conversions[conversion](value)如果 format_spec:值 = 格式(值,格式规格)结果.附加(值)返回 '​​'.join(result)

现在接受报价:

<预><代码>>>>s = "密钥是 {d['key']}">>>d = {'key': 'val'}>>>evaluate_template_expressions(s)'关键是val'

本质上,你可以用 eval(f'f{s!r}', globals()) 做同样的事情,但上面的可能会让你更好地控制你可能想要的表达式支持.

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?

解决方案

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.

这篇关于在变量中的python字符串上触发f字符串解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆