在Python变量中触发f-string解析。

5
这个问题涉及处理 Jupyter 魔法,但可以用更简单的方式表达。给定字符串 s = "the key is {d['key']}" 和字典 d = {'key': 'val'},我们想解析字符串。
旧方法是使用 .format(),但它会引发错误 - 它无法处理字典键。
"the key is {d['key']}".format(d=d)  # ERROR

我以为唯一的解决方法是将字典转化为对象(此处有解释在这里或在这里)。

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

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

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

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

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

它还处理函数 - 这是.format无法处理的。
f"this means {d['key'].lower()}"

虽然我们现在知道你可以使用.format,但我仍然想知道最初的问题:给定sd,如何强制解析f'string's?我添加了另一个示例,其中包含花括号中的函数,.format也无法处理,而f'string'则可以解决。

是否有.fstring()或可用的方法?Python内部使用什么?
2个回答

11

字符串格式化可以处理大部分字符串字典键,但您需要删除引号:

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

演示:

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

str.format()语法与Python表达式语法不完全相同(这是f-strings主要支持的语法)。

来自格式化字符串语法文档

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

并且

形如'[index]'的表达式将使用__getitem__()进行索引查找

语法有限,因为它会将任何只包含数字的字符串转换为整数,而其他所有字符始终被解释为字符串(虽然您可以使用嵌套的{}占位符从另一个变量动态插入键值)。

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

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)

现在报价被接受了:

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

实际上,您可以使用 eval(f'f{s!r}', globals()) 来执行相同的操作,但是上述方法可能会让您对想要支持的表达式有更多的控制。


谢谢 :) 仍然希望有一个实际的解决方案,但不知道这个! - Roelant
@Roelant:现在我已经将这部分内容加入了我的回答。 - Martijn Pieters
@MartijnPieters 我们如何对三引号 f-string 模板字符串执行 Formatter().parse(template) - HappyFace
@HappyFace:不确定你的意思。f-string三引号都只是语法上的差异,用于生成一个字符串,你也可以用不同的语法生成相同的值。Formatter().parse()只关心一个字符串,而不关心你如何生成这个值。然而,在f-string中使用的任何{...}占位符语法都已经被填充,因为这就是该语法的作用;立即执行表达式以填充占位符槽。没有模板可以从中提取。 - Martijn Pieters

0
给定sd,如何强制解析f'string's?是否有可用的函数或方法?
这可以通过使用eval来实现。 但要注意eval
>>> eval('f' + repr(s))
the key is val

repr 的作用是转义任何引号并用引号包装 s 本身。

如果您知道要格式化哪些变量(在这种情况下为 d),请选择 Martijn's answer 中的使用 str.format。由于 eval 的危险性,上述解决方案应该是最后的选择。


谢谢 :) 我知道eval,但我确实想避免使用它。 - Roelant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接