Skip to content(if available)orjump to list(if available)

Fstrings.wtf

Fstrings.wtf

76 comments

·July 19, 2025

tialaramex

String interpolation is one of those features like inference where if you've had it before then going without is very annoying, and so you add some and that's nicer, then you add more, each step seems like it's an improvement, and then one day you realise you're looking at unintelligible nonsense and you say "Oh no, what have we done?"

This is unusual, often CS would love to have as much of whatever as we can, but mathematics says no that's literally or practically impossible - but here both none and lots are awful and shouldn't be permitted.

One option, which Python and C# both picked is, well, leave it to taste. You can write sixteen pages of layered expressions in the uncommented interpolated string and it'll work, but your colleagues will curse your name and plot your destruction. Or at least you'll fail code review if you enforce such things.

Another option, in standard C++ 23 today for example, is refuse to take even the first step. You can have rich formatting, but standard C++ does not provide interpolation at all, if you want to format six parameters then pass them as parameters.

I'm happy with Rust's "Only a tiny bit of interpolation" where you can interpolate only identifiers, not any other expressions, but that's definitely more interpolation than some will be happy with, yet of course in some cases it's not quite enough.

Waterluvian

Purity and practicality are at odds and every language finds a different balance between the two. There is no one correct balance so busy minds will inevitably have loud opinions they want accepted as the one correct balance.

almostgotcaught

> busy minds

cute pun but compared to busybodies it really hides the implication (i thought you were talking about people with adhd at first).

veber-alex

I hate Rust's solution because it's not a solution at all.

Interpolation only works in a small subset of cases, which makes you constantly having to think whether it can or can't be used in the current situation and requires endless churn when refactoring code.

At the very minimum, they need to allow it to work with field access.

On the other hand, in python, while examples like this site exist and are funny/weird/quirky in practice nobody cares, and they just enjoy using fstrings.

Philpax

It's pretty straightforward? You can interpolate a variable in scope and apply modifiers to it. You can't interpolate arbitrary expressions (which field access would be). Alternatively, you can interpolate an arbitrary identifier, then specify a value for that identifier in the arguments.

The key is "identifiers, not expressions."

almostgotcaught

> String interpolation is one of those features

70% of these "wtfs" aren't about string interpolation but just python's syntax for string.format

https://docs.python.org/3/library/string.html#format-string-...

sublinear

> CS would love to have as much of whatever as we can, but mathematics says no

What does this have to do with either topic?

ejiblabahaba

Learned a few tricks that I'm sure are buried on fstring.help somewhere (^ for centering, # for 0x/0b/0o prefixes, !a for ascii). I missed the nested f-strings question, because I've been stuck with 3.11 rules, where nested f-strings are still allowed but require different quote characters (e.g. print(f"{f'{{}}'}") would work). I guess this got cleaned up (along with a bunch of other restrictions like backslashes and newlines) in 3.12.

F-strings are great, but trying to remember the minute differences between string interpolation, old-style formatting with %, and new-style formatting with .format(), is sort of a headache, and there's cases where it's unavoidable to switch between them with some regularity (custom __format__ methods, templating strings, logging, etc). It's great that there's ergonomic new ways of doing things, which makes it all the more frustrating to regularly have to revert to older, less polished solutions.

sfoley

Yeah I consider that one to be a trick question. I knew same-quote-style nested f-strings were coming, I just didn't know which version, and I still use the `f'{f"{}"}'` trick because I want my code to support "older" versions of python. One of my servers is still on 3.10. 3.11 won't be EOL until 2027.

nojs

> This is the first special feature of f-strings: adding a trailing equals sign lets you print out the expression and what it evaluates to.

    >>> foo='bar'; print(f"{foo=}")
    foo='bar'
Wow, never knew you could do that.

ck45

Python release notes are really worth reading, for me there's usually some "positive surprise"

The = support was added in Python 3.8: https://docs.python.org/3/whatsnew/3.8.html#f-strings-suppor...

carlhjerpe

If you come from verbosity land C# release notes are magically good as well, always some way to reduce boilerplate while maintaining "implicit verbosity" which your proprietary LSP resolves 100% correctly.

I'd prefer writing C# if I had the Linux interaction libs Python has. I'm too dumb to write syscall wrappers

acdha

It makes me sad that the PEP for the equivalent behaviour for function keyword arguments wasn’t accepted. It’s really common to see foo(bar=bar) and I think it’s not only cleaner but would help see subtle differences if that was foo(bar=) because it would make the cases where some arguments aren’t simply being passed through more obvious: foo(bar=, …, baaz=baaz.get_id()) avoids the most interesting detail being easily missed.

carlhjerpe

Do you know why? I didn't know of the fstring one either but I've thought to myself across many languages that a way to print the expression (or just varname in my head) with the result should exist.

akubera

The PEP: https://peps.python.org/pep-0736/

The discussion: https://discuss.python.org/t/pep-736-keyword-argument-shorth...

The rejection: https://discuss.python.org/t/pep-736-shorthand-syntax-for-ke...

Grammar changes, in particular things used everywhere like function invocations, have to be worth paying the price for changing/adding new rules. The benefits of fewer characters and more explicit intention weren't enough to outweigh the costs.

There were other considerations: Do linters prefer one syntax to another? Does the name refer to the parameter or the argument in tooling? Should users feel pressure to name local variables the same as the function's parameters? What about more shorthand for common cases like func(x=self.x, y=self.y)?

I personally did not like the func(x=, y=) syntax. I think their example of Ruby's func(x:, y:) would actually make more sense, since it's syntax that would read less like "x equals nothing", and more "this is special syntax for passing arguments".

roenxi

And it seems like a bad idea because of that wow factor - it isn't really adding enough to justify having surprising behaviour. It is more likely to be a bug than a feature.

It'd be better to just let people implement their own function that prints a subset of locals(), or provide a standard function that does the same.

ahartmetz

Incredibly common for debug output. In C++, I have made it a habit to just copy the expression, once with quotes and once without. It's informative and doesn't require thinking, or, well, I'm still working on that.

stkdump

It's the kind of thing you do with macros in C++.

black_puppydog

such a boon for print(f) debugging. :)

jszymborski

No more will I have to

print("foo", foo)

Twey

As someone who hasn't written Python in anger since before f-strings were a thing, I correctly guessed almost all the f-string specific syntax but made a bunch of errors that were just to do with the return values of various expressions. Maybe f-strings are the least wtf thing about Python? :)

ziml77

I don't really think there's anything "wtf" worthy about this. A lot of it isn't even about f-string behavior but about the "mini-language" for str.format()

recursivecaveat

I got 20/26. Only ones that really got me were walrus syntax collisions, the types with weirder padding behaviour, some format specifiers I wasn't familiar with. A lot of these are more trivia than wtfs, but not a bad quiz.

psychoslave

Awesome, makes me glad I didn't touch Python for years now.

Hell is paved with good will I guess. Probably Justine should update https://justine.lol/lex/

bogtog

Many of these are desirable features!

skrebbel

> makes me glad I didn't touch Python for years now.

C'mon, every language has quirks.

pansa2

Despite the URL, I’d only consider a few of these to be WTFs. Questions 20 & 21 definitely are, though:

  >>> a = 42
  >>> print(f"{a:=10}")
          42
  >>> print(f"{(a:=10)}")
  10
I still can’t believe anyone thought the walrus operator was a good idea.

almostgotcaught

this has nothing to do with the walrus operator and everything to do with how string.format works in python https://docs.python.org/3/library/string.html#format-string-...

Edit: someone downvoted me because they don't understand there is no walrus operator here

    print(f"{a:=10}")

lyu07282

They obviously meant the second example:

     >>> print(f"{(a:=10)}")

almostgotcaught

the output of the second example is "obvious" so i don't see the problem.

Ezhik

I learned a bunch of these when trying to make an f-string-like library for Lua [1], but `f"{...}" and the walrus ones caught me off-guard.

Glad this is nowhere near Wat [2], though.

[1]: https://ezhik.jp/f-string.lua/

[2]: https://www.destroyallsoftware.com/talks/wat

variadix

That library looks awesome

cluckindan

If this was JavaScript syntax, most of the comments would be lamenting the unintuitive syntax and weird features.

crazygringo

Right, I assumed the real point the quiz was making is that Python is as full of footguns as JavaScript, since I've seen this type of thing for JS a bunch of times.

Not saying I agree, but was definitely expecting that to be the main topic of discussion here...

jvolkman

But if it were Perl, they'd be celebrating.

jdranczewski

I usually refer to https://pyformat.info/, which doesn't have all this detail, but most of the reasonable stuff is included

noisy_boy

16/26 I rarely code in python but thought I knew f-strings well - evidently I don't. I am still thinking in 3.6 mode.