It's called "string literal interpolation". The string must be a literal, i.e. at the time the compilation takes place compiler will turn the string into a proper executable code. If you already have a string as a value (not as a literal), it's too late for that.
I don't have access to Python that has PEP 498 enabled, so my examples will be in Ruby, which has had this mechanism for a long time. The Ruby syntax for Python's f"...{expr}..."
is "...#{expr}..."
.
In Ruby, "a#{2 * 3}b"
is syntactic sugar for ["a", (2 * 3), "b"].join
(as in, they produce exactly the same bytecode). If you have the string "2 * 3"
already as a value, the compiler can't do anything about it; the only way to turn a string value into a result is to evaluate it.
In the first example, you have a string literal inside a string literal; both are processed by the compiler at compile time: when the compiler sees the outer literal, it compiles it, finds another string literal there, compiles that as well, produces code. As a matter of fact, "a#{"#{2 * 3}"}b"
produces exactly the same byte code, again.
The fact that this is done at compile time is also the reason why string literal interpolation will raise a syntax error if the expression inside is malformed, even if the line in question is never executed: if false; "#{1+}"; end
will produce a SyntaxError
.
The fact that this is done at compile time means strings already in variables are not eligible for this mechanism. In your code, by the time res
is evaluated, expr
could have been anything; the only way out is evil
(or another, safer, evaluator).