[Python-ideas] Let’s make escaping in f-literals impossible

Steven D'Aprano steve at pearwood.info
Tue Aug 30 12:34:39 EDT 2016


On Tue, Aug 30, 2016 at 11:43:03PM +1000, Chris Angelico wrote:
> On Tue, Aug 30, 2016 at 10:56 PM, Philipp A. <flying-sheep at web.de> wrote:
> > My issue is just that it’s as much of a string as a call of a (string
> > returning) function/method or an expression concatenating strings:
> >
> > ''.join(things)   # would you call this a string?
> > '{!r}'.format(x)  # or this? it’s basically the same as this “f-string”:
> > f'{x!r}'
> > 'start' + 'end'   # or this? it’s a concatenation of two strings, just like
> > f'start{ "end" }'
> 
> Yes, an f-string is really a form of expression, not a literal. But
> prior art has generally been to have similar constructs referred to as
> "interpolated strings" or similar terms:
> 
> https://en.wikipedia.org/wiki/String_interpolation

*shrug* A misleading name is misleading no matter how many people use 
it. If <insert the name of a language you dislike here> started calling 
function calls:

func(arg, x, y, z+1)

"s-strings" for "source code strings", because you write them as source 
code, and changed the syntax to 

func"arg, x, y, z+1"

we'd all recognise what a mistake it is to call this a string, string 
delimiters or not. The *result* of calling it may be a string, but the 
expression itself is a kind of function call.

[nasty thought]

Why don't we allow func"*args" as syntactic sugar for str(func(*args))?

[/remove tongue from cheek]


> Plenty of languages have some such feature, and it's usually
> considered a type of string. 

The result is a type of string. The expression is not.


> Notice the close parallels between actual
> string literals used as format strings ("I have %d apples" % apples)
> and f-strings (f"I have {apples} apples"), 

That's a misleading comparison. The *template string* is "I have %d 
apples" and that is just a string. The *format operator* is % and the 
formatting operation or expression is the entire expression.

Since f-strings can contain arbitrary expressions, the analogy is not 
with the template string, but a function (method or operator) call:

f"I have {fruit - bananas - oranges} apples"

is not equivalent to "I have {} apples", but to 

"I have {} apples".format(fruit - bananas - oranges) 

which is clearly a method call that merely returns a string, not a 
string itself.

Consequently, there's no way to delay evaluation of such an f-string, 
any more than you can delay evaluation of the expression[1]:

func(fruit, 2*bananas, 3/oranges)

You can't generate a template, and pass it to different environments to 
be formatted. If you need to evaluate f"I have {apples} apples" in six 
different contexts, you have to repeat yourself: it is equivalent to a 
function call, complete with implicit arguments, not a function object, 
and certainly not equivalent to a template string.


> and how this same parallel
> can be seen in many other languages. Yes, it may be a join expression
> to the compiler, but it's a string to the programmer.

Only if the programmer is fooled by the mere use of string delimiters. 
It really isn't a string. It is executable code that returns a string. 
It is basically a distant cousin to eval(), in disguise.

I really wish we had a better name for these things than f-strings :-(





[1] Tricks with eval() excluded.

-- 
Steve


More information about the Python-ideas mailing list