簡體   English   中英

如何將變量評估為Python f字符串

[英]How to evaluate a variable as a Python f-string

我想擁有一種評估f字符串的機制,其中要評估的內容位於變量內。 例如,

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

對於我考慮的使用情況,字符串s可能來自用戶輸入(其中eval信任用戶)。

雖然在生產中使用eval通常是非常不好的做法,但也有明顯的例外。 例如,用戶可能是在本地計算機上工作的Python開發人員,他想使用完整的Python語法來開發SQL查詢。

有關復制的注意事項: 這里這里也有類似的問題。 在模板的有限上下文中提出了第一個問題。 第二個問題盡管與此非常相似,但已被標記為重復。 由於此問題的上下文與第一個問題顯着不同,因此我決定根據第二個問題之后自動生成的建議來詢問第三個問題:

這個問題已經問過了,已經有了答案。 如果這些答案不能完全解決您的問題,請提出一個新問題。

即使使用受信任的用戶,使用eval也僅是最后的選擇。

如果您願意犧牲語法的靈活性來獲得更多的安全性和控制力,則可以使用str.format並為其提供整個范圍。

這將不允許對表達式求值,但是單個變量將被格式化為輸出。

x = 3
y = 'foo'

s = input('> ')
print(s.format(**vars()))

> {x} and {y}
3 and foo

這是我的嘗試,受kadee對類似問題的優雅回答的啟發,我對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

我可能還沒有注意到其他陷阱,但出於很多目的,我認為這已經足夠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM