【问题标题】:Is nested string literal interpolation possible?嵌套字符串文字插值是否可能?
【发布时间】:2018-11-18 01:35:27
【问题描述】:

在使用formatted string literal时,在一定程度上可以嵌套f-strings

a = 3
b = 7

res = f"{f'{a*b}'}"

print(res) # '21'

虽然,如果内部表达式是包含字符串的变量,则同样不起作用。

a = 3
b = 7

expr = 'a*b'

res = f"{f'{expr}'}"

print(res) # 'a*b'

有没有办法让这个工作和第二个输出也成为'21'?如果不是,第一个和第二个阻止它的字符串有什么区别?

【问题讨论】:

  • 澄清一下,您想知道是否可以让第二个输出 21?
  • 也许你可以把eval放进去,即res = f"{f'{eval(expr)}'}"
  • 如果有人用 eval 发布答案,他们会被否决。
  • @user3483203 我接受你的赌注并为你加注两个选项

标签: python string python-3.6


【解决方案1】:

有一些库已经开发出用于评估数值和逻辑表达式的函数安全(“安全”是关键)。

首先,设置——

a = 3
b = 7
op = '*'

numexpr.evaluate

>>> import numexpr as ne
>>> ne.evaluate(f'{a} {op} {b}')
array(21, dtype=int32)

numexpr 足够聪明,可以优化您的表达式,在某些情况下甚至比 numpy 更快。使用pip 安装。


pandas.eval

来自Pandas API 的安全 eval,类似于ne.evaluate

>>> import pandas as pd
>>> pd.eval(f'{a} {op} {c}')
12

【讨论】:

  • 你不是在 10 分钟前发誓使用 eval 否决任何问题的问题吗?
  • @OlivierMelançon 内置 eval,是的。我仍然会
  • 那是公平的,我更感兴趣的是知道 为什么 如果它实际上不能用 f 字符串完成,但你的回答很有趣。为什么 pandas.eval 被认为是安全的?
  • @OlivierMelançon 为什么它不起作用?我不确定,但我可以回答第二个问题。 pandas.eval 实际上有代码来解析它接收到的表达式,并且它对它认为有效的操作种类和不合法的操作种类是严格的。例如,对pd.eval 的导入语句是严格禁止的。
【解决方案2】:

我认为,在调用每个表达式时,了解内部实际发生的情况会很有帮助。

f"{f'{a*b}'}"

def om1(a, b):
    return f"{f'{a*b}'}"

dis.dis(om1)
  2           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_MULTIPLY
              6 FORMAT_VALUE             0
              8 FORMAT_VALUE             0
             10 RETURN_VALUE

外部 f-string 遇到一个它计算的表达式,内部 f-string 也找到一个它计算的表达式,这导致对 @ 的调用987654323@

f"{f'{expr}'}"

def om2(a, b):
    expr = 'a*b'
    return f"{f'{expr}'}"

dis.dis(om2)
  2           0 LOAD_CONST               1 ('a*b')
              2 STORE_FAST               2 (expr)

  3           4 LOAD_FAST                2 (expr)
              6 FORMAT_VALUE             0
              8 FORMAT_VALUE             0
             10 RETURN_VALUE

这里,第一个 f-string 遇到一个表达式并对其求值,而内部 f-string 遇到一个字符串,导致调用LOAD_FAST而不是将字符串的内容评估为 Python 代码。

另外,重要的是要注意在第二个示例中缺少对abLOAD_FAST 调用,它们在第一个示例中存在。

【讨论】:

  • 显示字节码是个好主意。这个答案真的很有帮助,但如果不是因为@Amadan 答案中给出的关键事实,f-string 代码是在编译时生成的,而不是在运行时生成并且它 确实是不够的不 依赖于调用评估。如果我能接受这两个答案,我希望它们结合起来给出最好的解释。
【解决方案3】:

它被称为“字符串文字插值”。字符串必须是文字,即在编译发生时编译器会将字符串转换为适当的可执行代码。如果您已经将字符串作为值(而不是文字),那就太迟了。

我无法访问启用了 PEP 498 的 Python,因此我的示例将使用 Ruby,该机制已经使用了很长时间。 Python 的 f"...{expr}..." 的 Ruby 语法是 "...#{expr}..."

在 Ruby 中,"a#{2 * 3}b"["a", (2 * 3), "b"].join 的语法糖(如,它们产生完全相同的字节码)。如果您已经将字符串"2 * 3" 作为值,编译器将无能为力;将字符串值转换为结果的唯一方法是对其求值。

在第一个示例中,字符串文字中有一个字符串文字;两者都由编译器在编译时处理:当编译器看到外部文字时,它会对其进行编译,在那里找到另一个字符串文字,然后对其进行编译,生成代码。事实上,"a#{"#{2 * 3}"}b" 再次生成完全相同的字节码。

这是在编译时完成的,这也是如果内部表达式格式错误,字符串文字插值会引发语法错误的原因,即使有问题的行从未执行过:if false; "#{1+}"; end 将产生 @987654328 @。

这是在编译时完成的,这意味着已经在变量中的字符串不适合这种机制。在您的代码中,在评估 res 时,expr 可能是任何东西;唯一的出路是evil(或另一个更安全的评估者)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-14
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    相关资源
    最近更新 更多