【问题标题】:How to evaluate a variable as a Python f-string如何将变量评估为 Python f 字符串
【发布时间】:2019-07-09 02:17:00
【问题描述】:

我希望有一种机制来评估 f 字符串,其中要评估的内容在变量中提供。例如,

x=7
s='{x+x}'
fstr_eval(s)

对于我想到的用例,字符串s 可能来自用户输入(用户信任eval)。

虽然在生产环境中使用eval 通常是非常糟糕的做法,但也有明显的例外。例如,用户可能是 Python 开发人员,在本地机器上工作,希望使用完整的 Python 语法来开发 SQL 查询。

关于重复的注意事项:有类似的问题herehere。第一个问题是在模板的有限上下文中提出的。第二个问题虽然与这个问题非常相似,但已被标记为重复。因为这个问题的上下文与第一个问题有很大不同,所以我决定根据第二个问题之后自动生成的建议提出第三个问题:

这个问题已经被问过并且已经有了答案。如果 这些答案不能完全解决您的问题,请提出新的问题 问题。

【问题讨论】:

    标签: python python-3.x python-3.6 string-interpolation f-string


    【解决方案1】:

    这是我在kadee's elegant answer 对类似问题的启发下对 f 字符串进行更稳健评估的尝试。

    不过,我想避免eval 方法的一些基本缺陷。例如,只要模板包含撇号,eval(f"f'{template}'") 就会失败,例如the string's evaluation 变为 f'the string's evaluation',其计算结果出现语法错误。第一个改进是使用三撇号:

    eval(f"f'''{template}'''")
    

    现在在模板中使用撇号(大部分)是安全的,只要它们不是三撇号。 (三引号也可以。)一个值得注意的例外是字符串末尾的撇号:whatcha doin' 变为 f'''whatcha doin'''',在连续第四个撇号处出现语法错误。下面的代码通过去除字符串末尾的撇号并在评估后将它们放回原处来避免这个特殊问题。

    import builtins
    
    def fstr_eval(_s: str, raw_string=False, eval=builtins.eval):
        r"""str: Evaluate a string as an f-string literal.
    
        Args:
           _s (str): The string to evaluate.
           raw_string (bool, optional): Evaluate as a raw literal 
               (don't escape \). Defaults to False.
           eval (callable, optional): Evaluation function. Defaults
               to Python's builtin eval.
    
        Raises:
            ValueError: Triple-apostrophes ''' are forbidden.
        """
        # Prefix all local variables with _ to reduce collisions in case
        # eval is called in the local namespace.
        _TA = "'''" # triple-apostrophes constant, for readability
        if _TA in _s:
            raise ValueError("Triple-apostrophes ''' are forbidden. " + \
                             'Consider using """ instead.')
    
        # Strip apostrophes from the end of _s and store them in _ra.
        # There are at most two since triple-apostrophes are forbidden.
        if _s.endswith("''"):
            _ra = "''"
            _s = _s[:-2]
        elif _s.endswith("'"):
            _ra = "'"
            _s = _s[:-1]
        else:
            _ra = ""
        # Now the last character of s (if it exists) is guaranteed
        # not to be an apostrophe.
    
        _prefix = 'rf' if raw_string else 'f'
        return eval(_prefix + _TA + _s + _TA) + _ra
    

    不指定求值函数,这个函数的局部变量是可以访问的,所以

    print(fstr_eval(r"raw_string: {raw_string}\neval: {eval}\n_s: {_s}"))
    

    打印

    raw_string: False
    eval: <built-in function eval>
    _s: raw_string: {raw_string}\neval: {eval}\n_s: {_s}
    

    虽然前缀 _ 减少了意外碰撞的可能性,但可以通过传递适当的评估函数来避免该问题。例如,可以通过lambda 传递当前全局命名空间:

    fstr_eval('{_s}', eval=lambda expr: eval(expr))#NameError: name '_s' is not defined
    

    或者更一般地,通过将​​合适的 globalslocals 参数传递给 eval,例如

    fstr_eval('{x+x}', eval=lambda expr: eval(expr, {}, {'x': 7})) # 14
    

    我还包含了一种机制,可以通过“原始字符串文字”机制选择是否应将\ 视为转义字符。例如,

    print(fstr_eval(r'x\ny'))
    

    产量

    x
    y
    

    同时

    print(fstr_eval(r'x\ny', raw_string=True))
    

    产量

    x\ny
    

    可能还有其他我没有注意到的陷阱,但出于许多目的,我认为这就足够了。

    【讨论】:

      【解决方案2】:

      即使是受信任的用户,使用eval 也只能是最后的手段。

      如果您愿意牺牲语法的灵活性以获得更多的安全性和控制力,那么您可以使用str.format 并为其提供整个范围。

      这将不允许对表达式求值,但会将单个变量格式化到输出中。

      代码

      x = 3
      y = 'foo'
      
      s = input('> ')
      print(s.format(**vars()))
      

      示例

      > {x} and {y}
      3 and foo
      

      【讨论】:

        猜你喜欢
        • 2022-01-19
        • 1970-01-01
        • 2010-09-28
        • 2014-08-31
        • 1970-01-01
        • 2011-03-30
        • 1970-01-01
        • 2013-06-01
        相关资源
        最近更新 更多