
Should all strings become f-strings? - pansa2
http://pyfound.blogspot.com/2020/04/all-strings-become-f-strings-python.html
======
Traster
I know this is just a personal feeling, but I prefer paying the cost of
writing f every time to have an indicator of where I'm expecting
substitutions, rather than having the default behaviour of the language be
that they're going to interfere with your string literals. It just feels like
doing something non-obvious by default is more likely to cause issues, whereas
the person forgetting the f in an f-string clearly knows about f-strings and
so is going to find their error much more easily. Having said that, I'm not a
someone who writes particularly pythonesque code in the first place so my
sensibilities don't carry a huge amount of weight.

~~~
ChrisSD
Talking of probably unpopular opinions: I'd prefer that all strings be raw by
default so escape characters aren't processed at all. Characters like `\n` or
`\uXXXX` can be useful sometimes but I'd prefer to opt in to that. In fact,
with f-strings its easy to simple use a constant instead of `\\` escape
sequences.

Admittedly this would be too big of a change to make now.

~~~
mantap
I think the way it works now is correct. Using escape characters is more
common than writing strings with backslashes. The more common case should be
default.

------
fhennig
So just because sometimes you forget to put a _single_ character in front of
your strings, you'd like to make a breaking change in the language that
affects one of the must fundamental data structures in the language?

I don't think thats a good idea.

~~~
searchableguy
I wonder if they can use a linter to catch it.

~~~
leetrout
Go linters catch it so I would bet yes they could but there might be a few
false positives.

------
dragonwriter
No, all strings shouldn't be f-strings, because if they were you'd either need
to resort to weird, awkward escaping or invent a new prefix to have template
strings that you interpolate repeatedly later, and in either case break
backward compatibility. Both template strings and immediately interpolated
strings are needed, and the current “normal string” vs. f-string divide allows
expressing both easily and clearly, and there is no improvement to that status
quo in this area that is worth the cost in backward compatibility.

~~~
pydry
The awkwardness of escaping makes me wonder what some vehemently pro f string
people are smoking. .format is a neat, clear, terse way of constructing
strings like so:

    
    
        description_string = "The {} is a problem, please look at: {}".format(
            some_function(value)[lookup],
            ' '.join([item for item in list_of_items]),
        )
    

Where, with f strings, you'd need to do some nasty escaping or create
descriptive labels for the substitutions that were previously unnecessary.

With the second option you also get the problem of a potential loss of
cohesion as the variable 'name = some_function(value)[lookup]' won't be
intrinsically tied to the string where it is being used and tends to float
away from the string where it's actually being used. e.g.

    
    
       name = some_function(value)[lookup]
    
       [ 50 lines of code ]
    
       description_string = f"{name} is a problem..." etc.
    

Which creates additional problems with readability, bugs, etc.

~~~
Mister_Snuggles
Your first example works fine as an f-string without resorting to escaping,
etc:

    
    
        >>> def some_function():
        ...   return ['a','b','c']
        ...
        >>> list_of_items = ['one','two','three']
        >>> s = f"The {some_function()[1]} is a problem, please look at: {' '.join([item for item in list_of_items])}"
        >>> s
        'The b is a problem, please look at: one two three'
        >>>
    

I'll admit that it's somewhat ugly, but there's no escaping needed.

~~~
pydry
Yep and then as soon as you add quotation marks it breaks down:

    
    
        s = f"The {some_function()["WHOOPS!!!"]} is a problem, please look at: {' '.join([item for item in functino_call("whoops again")])}"
    

This indirectly proves the point I think.

------
jorams
> Larry Hastings pointed out that PHP strings are format strings by default
> and "the script kids love it."

This seems like an odd argument. PHP makes a clear distinction between string
literals with single quotes and those with double quotes, where only double
quotes do variable substitution and escape sequences. It's very common for PHP
projects to default to single quotes and only use double quotes when their
special handling is actually used.

~~~
zem
ruby does this too, and I think it's a great design. I know it's too late for
python to go that route, but I wish they had way back when, rather than having
single and double quotes be the same thing.

~~~
BerislavLopac
Python's mechanism for this is using a single-character prefix, which would
still be needed even if it followed the Ruby/PHP syntax -- simply because
Python has at least four different types of string literals:

    
    
        * f"f-strings", which do both substitution and escaping
        * regular "strings", which do escaping but no substitution
        * r"r-strings" (for "raw"), which do neither
        * b"b-strings", which are actually not strings but sequences of bytes

~~~
pansa2
There are also `fr`-prefixed strings, which do substitution but no escaping,
and `br`-prefixed raw byte sequences.

~~~
BerislavLopac
Excellent point, thank you!

There are also the u"u-strings" which were reintroduced to reduce the workload
when converting the Python 2 programs; unfortunately they forgot to do the
same for the relatively common (at least when defining regular expressions)
ru"ru-strings" and ur"ur-strings"...

------
mcherm
If I were creating a new language today there is absolutely no question in my
mind: I would have the default string type support interpolation. It means
that there is a particular character or character combination ("{" for python,
"${" for JavaScript, etc) which is not allowed in strings without escaping --
that isn't much of a usability problem: we live with the need to escape new
lines in strings and no one even thinks twice.

But changing it in an existing language causes massive backward compatibility
issues. Unlike the move from Python 2 to Python 3, it is trivial to build a
perfect migration script (2to3 was useful but not perfect) -- it just needs to
prefix "p" in front of all normal strings, or at least in front of all normal
strings that contain the interpolation character(s).

But even so, the need to run that script is a VERY BIG DEAL. A perfect
migration script doesn't help with sample code that is printed in a book, or
thousands of other situations that are awkward to fix. It would, without a
doubt, require very careful introduction by use of "from __future__ import".
(The fact that Python has that facility is extremely helpful for this kind of
migration.)

I am utterly bewildered by this article's claim that the proponent of this
change thinks it should be dropped if it is to be introduced carefully using
"from __future__ import". That is just stupid.

I would rather end up in a place where interpolation strings are the default,
but it is not clear to me whether the path to get there is worth the pain.

------
eesmith
I know I'm one who keeps having to go back to add the 'f' prefix to my
strings.

I lived through the many "from __future__ import" of late Python 2.x releases,
so I know I can deal with a "all_fstrings" solution during a migration period.

Looking through my code I see things like:

    
    
       r"[a-zA-Z][a-zA-Z0-9]{3}"
    

which, if interpreted as an f-string, would change the regexp but not cause it
to not compile - the breakage would be downstream.

I suspect there's some places where existing code which uses .format() will
still work, with all-f-strings, like:

    
    
      >>> name = "eesmith"
      >>> print("Who? {name}".format(name=name))
      Who? eesmith
      >>> print(f"Who? {name}".format(name=name))
      Who? eesmith
    

but cause subtle errors because of the double string formatting:

    
    
      >>> name = "{x}"
      >>> print("Who? {name}".format(name=name))
      Who? {x}
      >>> print(f"Who? {name}".format(name=name))
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      KeyError: 'x'
    

I'm glad I don't have to make this decision.

------
jpxw
I think the current explicit f-string syntax actually aids readability. When I
see an f-string, I’m immediately aware that some interpolation is taking
place, and my eyes can quickly scan for the braces. Additionally, it helps me
to differentiate between “static content” and “dynamic content”, for want of a
better phrase.

------
roelschroeven
Please for the love of f"{deity}" don't do this. Making the language more
complex, introducing p-string, just because people sometimes forget the f
prefix? And they're not going to forget the p prefix?

I don't even like f-strings all that much. Sure, f'a = {a}' is nice and clean,
but

    
    
        f'sin(a) = {math.sin(a)}; cos(a) = {math.cos(a)}'
    

is IMHO worse than

    
    
        'sin(a) = {}; cos(a) = {}'.format(
            math.sin(a),
            math.cos(a),
            )
    

and it only gets worse for longer strings and/or more complex expressions.
Python already had 3 ways of formatting strings (%, .format(), string.Template
(which nobody uses)). Adding a 4th that IMHO is only a very small improvement
over .format() and that only in a limited number of use cases, was not really
worth the effort. What happened to "There should be one-- and preferably only
one --obvious way to do it"?

But making f-strings the default is 1000 times worse. Trading one set of
errors for another one, making the language unnecessarily more complex in the
process. Blurring the line between a string and formatting a string. What
happened to "Explicit is better than implicit"? Bah.

------
01walid
No. Explicit is better than implicit.

~~~
axegon_
This. I honestly love the f-string syntax but the fact that there is a clear
separation is what makes them appealing - I find it far more convenient to
have f"{v1}, {v2}, ... {vN}" vs "{v1}, {v2}, ... {vN}".format(v1, v2, ... vN),
not to mention % v1, v2, ... vN.

The way I see it, currently:

* The separation makes them readable and easy to understand.

* Making them implicit will cause hell for many people who rely on user input (and I'm certain a lot would try to exploit that).

* The fact that they are readable, makes them easier to debug, as opposed to trying to figure out where did the "{hey ma look at what i can do}" SyntaxError came and who did what exactly.

------
hawski
If f-strings would become default, I would hope for from __past__ import...

------
Macha
I feel like this proposal didn't learn its lesson from the need to port u''
string prefixes to Python 3.2. How would one write a string that should not
need interpolation in current Python versions so it doesn't silently get
interpolated in Python 3.defaultfstrings? I also worry about codebases that
don't get updated, seem to work, but silently open up attacks:

like the following:

    
    
        username = some_request_variable_1
        password = some_request_variable_2
        sql = 'INSERT INTO users (username, password) VALUES ({username}, {password})'.format(
            escape(username),
            hash(password, salt, per_user_salt)
        )'
    

Good code? No. Exists in the real world? Almost certainly. Safe in Python
currently? Assuming escape is implemented correctly. Safe in f-strings by
default world? No.

This also seems way less pressing than "most Python programs silently handle
unicode wrong" as a reason to change the language.

------
pansa2
I recently discovered that Python supports four different kinds of string
literal (plus two kinds of `bytes` literal). Strings can have no prefix, or be
prefixed with `f`, `r` or `fr`. This proposal simply changes the prefixes to
`p`, none, `pr` and `r` respectively.

Do we really need all four types? In particular, I expect use of the last type
to be very rare.

Isn't it common in other languages to only have the second and third types?
That is, they allow you to choose between using both escape sequences and
variable interpolation, or using neither - there's no way to get one without
the other?

~~~
j0057
And don't forget we can do string formatting four ways now!

    
    
        >>> a = 42
        >>> b = 'bar'
        >>> 'foo %03d %r' % (a, b)
        "foo 042 'bar'"
        >>> 'foo {:03d} {!r}'.format(a, b)
        "foo 042 'bar'"
        >>> f"foo {a:03} {b!r}"
        "foo 042 'bar'"
        >>> __import__('string').Template('foo $a $b').substitute(locals())
        'foo 42 bar'

------
Grue3
I don't think we have a single f-string in the entire Python 3.7 codebase in
the project I'm working on. They seem rather useless to me. I often pass a
string like 'this is a template with {var}' into another function that uses
.format(var=something) to fill out the template. This seems impossible with
f-strings. On the other hand everything that f-string can do can be just as
easily done with .format, except in a more explicit and Pythonic way.

------
Nicksil
No. Please no. There's no need for this.

The reasoning behind this is because _some_ people _occasionally_ forget to
include the `f` prefix? Then there's this:

>Smith would add a new "p" string prefix for plain strings, which would behave
like ordinary strings today.

So what happens when reports start rolling in when _some_ people
_occasionally_ forget to include the `p` prefix?

------
muglug
> Larry Hastings pointed out that PHP strings are format strings by default
> and "the script kids love it."

This is not quite true. PHP treats strings differently based on the quote
character used - ' delineates plain strings and " delineates format strings.

------
pansa2
Can anyone explain what happened to this submission? There was no interest
when it was first posted, then at 20 hours old its submission time was reset
and it was suddenly on the front page.

Then, at 24 hours old, it has suddenly dropped to page 6?

~~~
eesmith
I believe that's moderator intervention. When they think a topic is overlooked
and deserving, they reset the timer to give it another change.

------
benibela
More important than f-strings are translatable strings.

When you write print(f"The value is {x}"), it cannot be translated.

Perhaps they could add a new prefix, like t"The value is {x}", and then gather
all such strings for translation?

~~~
eesmith
See the gettext module, at
[https://docs.python.org/3/library/gettext.html](https://docs.python.org/3/library/gettext.html)
, where gettext() is usually aliased to "_()" so people can write _("int'l
string") . There are also tools to extract such strings.

You are correct - translatable strings require late-binding, including
possible re-ordering. As the Django documentation points out: 'an English
translation may be "Today is November 26.", while a Spanish translation may be
"Hoy es 26 de noviembre." – with the month and the day placeholders swapped.'

It adds that f-strings aren't currently supported... And I don't see how they
could be supported.

------
globular-toast
f-strings should have been the default right from the start, with a prefix
(like r"") to turn it off. In that way it would be similar to bash. But I
think that ship has sailed. Like any mature language Python will have to live
with some of the unfortunate decisions of the past. Thankfully Python is still
far better than most languages in that regard.

------
cpach
Does anyone know if this is actively discussed at the moment? If so, on what
mailing list?

~~~
tda
Guido discussed the latest language summit on this podcast:
[https://pythonbytes.fm/episodes/show/179/guido-van-rossum-
dr...](https://pythonbytes.fm/episodes/show/179/guido-van-rossum-drops-in-on-
python-bytes). I believe he mentioned default f-strings were discussed and
rejected.

------
eternauta3k
Sorry, no more breaking changes after 2->3.

~~~
smcl
Breaking changes will inevitably be necessary in any language. Take the
recent-ish discussions about cleaning out the deadweight from the Python
stdlib, there's a bunch of modules that are basically unused by users,
untouched by maintainers and are therefore effectively deprecated. Taking a
"no more breaking changes" philosophy means continuing to semi-support these
into eternity, when realistically many can be cast off with a reasonable
notice period and a well-documented migration path.

~~~
selectnull
I think we should not think in absolutes! There are breaking changes that are
small and acceptable, and there are those that will require unnecessary
changes to source code to port them to new version. I don't think python
community has yet recovered from 2to3; and I'm not saying it shouldn't have
been done; I'm saying I wouldn't like a repeat in 3to4.

As for stdlib, if I'm not mistaken, those packages will be published as
standalone packages and simple `pip install` will be a migration path.

~~~
smcl
We are in complete agreement.

However, while I’m undecided on “all strings should be f-strings” I do know
that “no more breaking changes” is not realistic and is a good way to ensure
either a dead language or one with a hell of a lot of baggage

~~~
selectnull
Personally, I'm against "all strings should be f-strings" just because it
would cause changes to source code that I don't find necessary.

When Python 3.0 came out, u"" prefix was abolished and every u-string had to
be changed. That change was found later to be a mistake and was reverted (I'm
not sure what python version reverted it, somewhere around 3.3 I think). This
proposal reminds me of that: change that seemingly sound good at first, but is
simply not worth it.

Yes, there will be breaking changes and I'm not against them. I'm just against
this one.

------
steerablesafe
No

