【问题标题】:How can I use f-string with a variable, not with a string literal?如何将 f-string 与变量一起使用,而不是与字符串文字一起使用?
【发布时间】:2019-06-18 11:44:42
【问题描述】:

我想将 f-string 与我的字符串变量一起使用,而不是使用字符串字面量定义的字符串 "..."

这是我的代码:

name=["deep","mahesh","nirbhay"]
user_input = r"certi_{element}" # this string I ask from user  

for element in name:
    print(f"{user_input}")

这段代码给出了输出:

certi_{element}
certi_{element}
certi_{element}

但我想要:

certi_{deep}
certi_{mahesh}
certi_{nirbhay}

我该怎么做?

【问题讨论】:

  • 不希望用户能够执行任意代码。使其成为字典查找或类似的东西。
  • @user3483203:用户在哪里声明他们希望能够使用任意变量?这里没有什么可以说可以使用任何名称。
  • @MartijnPieters 我的评论与任意变量无关。我是说他们不应该希望评估用户输入中的表达式。
  • @user3483203:是的,我不知道 OP 是否期望完整的表达式支持(或者甚至知道该功能存在)。他们要求一个变量,所以str.format() 绰绰有余。

标签: python python-3.x string string-formatting f-string


【解决方案1】:

f"..." 字符串在将表达式结果插入 literal 时非常有用,但您没有文字,您在单独的变量中有一个模板字符串。

您可以使用str.format() 将值应用于该模板:

name=["deep","mahesh","nirbhay"]
user_input = "certi_{element}" # this string i ask from user  

for value in name:
    print(user_input.format(element=value))

使用名称(例如{element})的字符串格式占位符不是变量。您可以在 str.format() 调用的关键字参数中为每个名称分配一个值。在上面的例子中,element=value 传入了value 变量的值,用element 填充占位符。

f-strings 不同,{...} 占位符是 not 表达式,您不能在模板中使用任意 Python 表达式。这是一件好事,您不希望最终用户能够在您的程序中执行任意 Python 代码。有关详细信息,请参阅Format String Syntax documenation

你可以传入任意数量的名字;字符串模板没有 使用它们中的任何一个。如果将str.format()**mapping 调用约定结合使用,则可以使用任何字典作为值的来源:

template_values = {
    'name': 'Ford Prefect',
    'number': 42,
    'company': 'Sirius Cybernetics Corporation',
    'element': 'Improbability Drive',
}

print(user_input.format(**template_values)

以上将允许用户在他们的模板中使用template_values 中的任何名称,他们喜欢任意次数。

虽然您可以使用locals()globals() 来生成将变量名映射到值的字典,但我不推荐这种方法。使用像上面这样的专用命名空间来限制可用的名称,并为您的最终用户记录这些名称。

【讨论】:

  • 感谢很好的解释
  • 如果我不知道用户在 { } 中给出的内容怎么办。在我们的例子中:元素。
  • @DeepGhodasara:您仍然需要告诉用户使用特定名称。虽然您可以parse out what names are used in a template,但您仍然必须将这些名称映射到值。我已经添加了如何让某人访问他们可以从中选择的多个名称。
【解决方案2】:

如果你定义:

def fstr(template):
    return eval(f"f'{template}'")

那么你可以这样做:

name=["deep","mahesh","nirbhay"]
user_input = r"certi_{element}" # this string i ask from user  

for element in name:
    print(fstr(user_input))

作为输出给出:

certi_deep
certi_mahesh
certi_nirbhay

但请注意,用户可以在模板中使用表达式,例如:

import os  # assume you have used os somewhere
user_input = r"certi_{os.environ}"

for element in name:
    print(fstr(user_input))

你绝对不想要这个!

因此,一个更安全的选择是定义:

def fstr(template, **kwargs):
    return eval(f"f'{template}'", kwargs)

任意代码不再可能,但用户仍然可以使用字符串表达式,例如:

user_input = r"certi_{element.upper()*2}"

for element in name:
    print(fstr(user_input, element=element))

作为输出:

certi_DEEPDEEP
certi_MAHESHMAHESH
certi_NIRBHAYNIRBHAY

这在某些情况下可能是需要的。

【讨论】:

  • 这是真正回答问题的唯一答案 - 如何使用带变量的 f 字符串格式。
  • 这只是使用template.format(**kwargs) 的一种非常低效的方式。更不用说不安全了,eval() 允许用户运行任意表达式。
  • @MartijnPieters,你的意思是我上面定义的第二个“更安全”的函数比 f-string 本身更不安全吗?
  • @kadee:是的,因为 f-string 是 static,它的表达式是预先确定的并且是不变的。使用eval(),您可以打开任意表达式的大门,让用户可以完全访问您的进程以及您的进程具有的访问级别。
  • 尽管提及这暗示的安全风险很重要,但这是该问题的正确答案。说,所以 - 我不会将它添加到公共使用代码中 - 但我发现它在从模板自动生成文件时非常有用(例如,为 HDL 生成自动测试台)。如果风险是一个问题,解决方法可能是预解析输入字符串 - 但不是 100% 无风险。风险与在您的应用程序中运行未知的 python 脚本相同。
【解决方案3】:

如果您希望用户有权访问您的命名空间,您可以这样做,但后果完全由您自己承担。除了使用 f 字符串,您可以使用 format 方法进行动态插值,语法非常相似。

如果您希望用户只能访问少量特定变量,您可以执行类似的操作

name=["deep", "mahesh", "nirbhay"]
user_input = "certi_{element}" # this string i ask from user  

for element in name:
    my_str = user_input.format(element=element)
    print(f"{my_str}")

您当然可以重命名用户输入的键与您使用的变量名称:

my_str = user_input.format(element=some_other_variable)

您可以让用户访问您的整个命名空间(或至少大部分)。请不要这样做,但请注意,您可以:

my_str = user_input.format(**locals(), **globals())

我使用print(f'{my_str}') 而不是print(my_str) 的原因是为了避免文字大括号被视为进一步的错误扩展的情况。比如user_input = 'certi_{{{element}}}'

【讨论】:

  • 为什么假设element这个名字是任意的
【解决方案4】:

我正在寻找与您的问题类似的东西。 我遇到了另一个问题的答案:https://stackoverflow.com/a/54780825/7381826

根据这个想法,我调整了您的代码:

user_input = r"certi_"

for element in name:
    print(f"{user_input}{element}")

我得到了这个结果:

certi_deep
certi_mahesh
certi_nirbhay

如果您宁愿坚持问题中的布局,那么最后的编辑就可以了:

for element in name:
    print(f"{user_input}" "{" f"{element}" "}")

阅读所有其他问题的安全问题,我认为这种替代方案没有严重的安全风险,因为它没有使用 eval() 定义新函数。

我不是安全专家,所以如果我错了,请纠正我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 2019-11-06
    • 1970-01-01
    • 2016-04-24
    • 2016-09-11
    • 2020-06-12
    相关资源
    最近更新 更多