Evaluation of variable as f-string

Chris Angelico rosuav at gmail.com
Fri Jan 27 18:41:40 EST 2023


On Sat, 28 Jan 2023 at 10:08, Rob Cliffe via Python-list
<python-list at python.org> wrote:
>
> Whoa! Whoa! Whoa!
> I appreciate the points you are making, Chris, but I am a bit taken
> aback by such forceful language.

The exact same points have already been made, but not listened to.
Sometimes, forceful language is required in order to get people to
listen.

> If it's addressed to me:  How about if I wanted a program (a learning
> tool) to allow the user to play with f-strings?
> I.e. to type in a string, and then see what the result would be if it
> had been an f-string?
> I suspect there are other use cases, but I confess I can't think of one
> right now.

Use the REPL, which will happily evaluate f-strings in their original
context, just like any other code would. You're already eval'ing, so
it's exactly what you'd expect. This is not the same thing as "typing
in a string", though - it's typing in code and seeing what the result
would be. (Except to the extent that source code can be considered a
string.)

> If it's addressed to me: "it" means a function that will take a string
> and evaluate it at runtime as if it were an f-string.  Sure, with
> caveats and limitations.

And that's what I am saying is a terrible terrible idea. It will
evaluate things in the wrong context, it has all the normal problems
of eval, and then it introduces its own unique problems with quote
characters.

>  And indeed Thomas Passim found this partial
> solution on Stack Overflow:
> def effify(non_f_str: str):
>      return eval(f'f"""{non_f_str}"""')

You can find anything on Stack Overflow. Just because you found it
there doesn't mean it's any good - even if it's been massively
upvoted.

> Addressing your points specifically:
>      1) I believe the quote character limitation could be overcome. It
> would need a fair amount of work, for which I haven't (yet) the time or
> inclination.

No problem. Here, solve it for this string:

eval_me = ' f"""{f\'\'\'{f"{f\'{1+2}\'}"}\'\'\'}""" '

F-strings can be nested, remember.

>      2) Yes in general you would have to pass it one dictionary, maybe
> two.  I don't see this as an insuperable obstacle.  I am not sure what
> you mean by "can't be done with full generality" and perhaps that's not
> important.

>>> def func():
...     x = 1
...     class cls:
...             y = 2
...             print(f"{x=} {y=}")
...             print(locals())
...
>>> func()
x=1 y=2
{'__module__': '__main__', '__qualname__': 'func.<locals>.cls', 'y': 2}

Maybe you don't care. Maybe you do. But locals() is not the same as
"all names currently available in this scope". And, this example is
definitely not something I would recommend, but good luck making this
work with eval:

>>> def func():
...     x = 1
...     print(f"{(x:=2)}")
...     print(x)
...
>>> func()
2
2
...     x = 1
...     print(eval("(x:=2)", globals(), locals()))
...     print(x)
...
>>> func()
2
1

>      3) Not sure I understand this.

Before f-strings existed, one of the big problems with "just use
str.format_map" was that you can't just pass it locals() to get all
the available names. You also can't eval arbitrary code and expect to
get the same results, even if you pass it globals and locals. And
various other considerations here - the exact issues seen here, but
flipped on their heads. So the obvious question is: why not just use
str.format_map?

> > Well, yes. If you asked "how can I do X", hoping the answer would be
> > "with a runtime-evaluated f-string", then you're quite right - the
> > answer might not be what you were hoping for. But since you asked "how
> > can I evaluate a variable as if it were an f-string", the only
> > possible answer is "you can't, and that's a horrible idea".
> I hope that I have shown that this is a somewhat dogmatic response.

And I hope that I have shown that it is fully justified.

> > Don't ask how to use X to do Y. Ask how to do Y.
> Good advice.

Exactly. As I have shown, asking how to use f-strings to achieve this
is simply not suitable, and there's no useful way to discuss other
than to argue semantics. If we had a GOAL to discuss, we could find
much better options.

ChrisA


More information about the Python-list mailing list