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 · 20h ago
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 · 18h ago
> busy minds
cute pun but compared to busybodies it really hides the implication (i thought you were talking about people with adhd at first).
whartung · 10h ago
> then one day you realise you're looking at unintelligible nonsense and you say "Oh no, what have we done?"
I think that’s a bit of what the Java String Template folks went through.
It was a pretty cool system (on the surface, not having used it, I liked what I saw), but they pulled it straight out. They felt the direction was unworkable. Pretty interesting considering I think the demand for such a facility (as mentioned, once tasted, hard to let cider), plus the work involved to date. Interesting they found it irredeemable to shelve it entirely and back to the white board.
PaulHoule · 10h ago
I tend to stuff complicated strings (SQL queries and such) into resources (files that get baked into the JAR) and implement some kind of templating for them if I think it's necessary.
edoceo · 7h ago
With proper escape (un-taint) on any user input, right?
orthoxerox · 13h ago
> One option, which Python and C# both picked is, well, leave it to taste.
Every time I need to format a number or a date in C#, I just hit the documentation. I refuse to memorize its terrible formatting mini language.
Timwi · 7h ago
Or you could just write your own if you think you can create one you'll be happier with. C# provides IFormatProvider for this exact purpose and I've honestly not seen that level of customizability elsewhere.
zahlman · 13h ago
> 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?"
I've never found it difficult to keep them in check. I still can't fathom a use case for nesting f-strings within an f-string substitution, for example. And probably the only format specifier I commonly use is `:02x`.
veber-alex · 19h ago
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 · 18h ago
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."
Timwi · 7h ago
The person you're responding to didn't misunderstand (or ask) how it works. They just find it inconvenient.
It would be perfectly possible to add field access without needing to support arbitrary expressions. However, as someone coming from a C# background, I would very much prefer arbitrary expressions.
sublinear · 19h ago
> 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?
tialaramex · 12h ago
Take optimisation, another task like interpolation which the machine does to transform our program text into an executable
We'd like exhaustive optimisation, in fact that can't be done and today we're used to a relatively aggressive optimisation which would not have been possible fifty years ago, but we're nowhere close to optimal for non-trivial programs.
Or correctness, it seems as though global static analysis should be possible, at least for something modest like a typical Unix utility - nope, that is known to be mathematically impossible for even quite modest software, only local analysis is at least plausible and its effects are much more limited than we might hope.
cvoss · 15h ago
I, too, struggled to understand the meaning of this sentence. I can parse it, I guess, but any meaning I try to guess for it is either implausible or irrelevant.
almostgotcaught · 19h ago
> String interpolation is one of those features
70% of these "wtfs" aren't about string interpolation but just python's syntax for string.format
"String interpolation" is a term for the purpose that the string.format syntax serves.
almostgotcaught · 10h ago
no it's not - string interpolation means inlining values and such. format strings long predate string interpolation https://en.wikipedia.org/wiki/Printf
oasisaimlessly · 9h ago
Python 2.7 called its % operator the "string interpolation" operator[1], and it doesn't allow inline values.
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.
zahlman · 13h ago
> I guess this got cleaned up (along with a bunch of other restrictions like backslashes and newlines) in 3.12.
One thing I keep not understanding is I see colleagues or AIs put f-strings in logger calls! Doesn't the logger do lazy interpolation? Doesn't this lose that nice feature?
mixmastamyk · 9h ago
Yes, but won’t matter unless in a tight loop. I usually use % with logging but once in a while use f’’ if easier or needed for some reason.
sfoley · 20h ago
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 · 22h ago
> 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 · 21h ago
Python release notes are really worth reading, for me there's usually some "positive surprise"
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 · 20h ago
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 · 19h ago
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.
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".
0cf8612b2e1e · 15h ago
Guess I will chime in for the dissent: there is already so much Python language. The more special gotchas that are added just for these minor wins is not worth it. In fact, if I ever saw a function that was foo(bar=) I would correct that to be explicit immediately. ‘import this’ and all that.
Said as a curmudgeon that has never used a walrus.
roenxi · 19h ago
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 · 21h ago
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 · 20h ago
It's the kind of thing you do with macros in C++.
ahartmetz · 14h ago
Yeah probably, I mean I thought of it but never went as far as actually adding something.
black_puppydog · 21h ago
such a boon for print(f) debugging. :)
jszymborski · 20h ago
No more will I have to
print("foo", foo)
pansa2 · 19h ago
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.
sfink · 16h ago
You can pry the walrus operator from my cold dead hands.
m = re.match(pattern1, line)
if m:
do_stuff(m.group(1))
else:
m = re.match(pattern2, line)
if m:
do_other_stuff(m.group(2))
else:
m = re.match(pattern3, line)
if m:
do_things(m.groups())
else:
m = ...
Obviously, there are ways to improve this particular example without using the walrus. But it's also usually not this simple.
I run into something like this allthetime. Not many times per program, and I don't actually use the walrus all that often, but when it's the right thing it's so very nice.
pansa2 · 7h ago
The walrus operator hardly changes that example at all:
if m := re.match(pattern1, line):
do_stuff(m.group(1))
else:
if m := re.match(pattern2, line):
do_other_stuff(m.group(2))
else:
if m := re.match(pattern3, line):
do_things(m.groups())
else:
m = ...
I've found `:=` useful in the headers of `while` loops, but that's about it. The complexity that walrus adds to the language far outweighs its usefulness.
ghewgill · 6h ago
The walrus operator allows you to use `elif` there to avoid cascading indentation.
if m := re.match(pattern1, line):
do_stuff(m.group(1))
elif m := re.match(pattern2, line):
do_other_stuff(m.group(2))
elif m := re.match(pattern3, line):
do_things(m.groups())
else:
...
sfink · 5h ago
...and for me, the benefit is that this is much more clear to me when reading the code. An increasing level of indentation directly maps to needing to keep more in my head. (Sure, in this case you can pattern match and see that there's nothing else, but in general you need to watch out for cases where the else may be doing more than just the next check on the same inputs. )
But the walrus is good even in the absence of a series of matches. It is quite common to need to know more than the boolean "did this match or not?", and so to my eye it is much cleaner to have one line doing the test and the next lines using the results. I don't care about saving characters or even lines. `m = ...match...` immediately followed by `if m:` feels like overhead for the sake of the programming language's limitations; it's extraneous to what I want to express.
Also, I assert that the pattern is better for correctness. It's like Rust's `if let Some(foo) = f() { ... }` pattern: guard on a certain condition, then use the results if that condition is true.
v = f()
if v:
...use v...
invites injecting code in between the assignment and the test. They're two different things to the reader. And it's not as clear that `v` should only be used in the consequent body.
if v := f():
...use v...
says exactly what it means: `f()` may return a truthy value. If it does, do something with it.
pansa2 · 6h ago
Ah, I missed that. Thanks
callc · 13h ago
Even though I’ve never used python walrus, I’m a huge fan of the “X language feature can be used as an expression”
I feel the same way with rust control flow being used as an expression, and am disappointed when other languages don’t have it.
Even though one could argue it makes things more complicated, I feel more mental freedom and feel the language getting out of the way.
marcosdumay · 14h ago
You know, creating functions is allowed in Python. As are loops.
The assignment is the least problematic thing on that code. I do even disagree on it being a problem.
Edit: someone downvoted me because they don't understand there is no walrus operator here
print(f"{a:=10}")
lyu07282 · 17h ago
They obviously meant the second example:
>>> print(f"{(a:=10)}")
almostgotcaught · 17h ago
the output of the second example is "obvious" so i don't see the problem.
pansa2 · 7h ago
Neither of the examples is problematic on its own; it's the fact that the language supports both of them. They're very hard to distinguish at-a-glance yet behave entirely differently.
Ezhik · 19h ago
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.
There are many mistakes in the Wat talk from destroyallsoftware. The biggest one is that he types `{}+[]` and claims that he's adding an object and a list. It's actually an empty scope followed by a unary + applied to a list. You can convince yourself of that by noting that `({}+[])` gives different output.
A smaller mistake that I find nonetheless amusing is that he uses Array(16) hoping to get 16 string separators. Oops, off by one error :)
Ezhik · 6h ago
Arguably that first one is just substituting one Wat for another.
variadix · 18h ago
That library looks awesome
Twey · 19h ago
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 · 17h ago
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()
0cf8612b2e1e · 15h ago
There were a few wats in there, but I agree that most of the test was if you know all of the string formatting syntax.
cluckindan · 22h ago
If this was JavaScript syntax, most of the comments would be lamenting the unintuitive syntax and weird features.
crazygringo · 21h ago
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...
va1a · 16h ago
More specific shenanigans aside, JavaScript will always be the king of unintuitive syntax. Some of these f-string tidbits are very much strange, but you'd have to be implementing something specific to encounter them. Meanwhile over in JS you're still waiting for your dependencies to install so you can compare two arrays.
cluckindan · 15h ago
That is exactly the kind of unfounded, strawman-riddled criticism I was after ;-)
What does an algorithmic task such as array comparison have to do with language syntax? The answer is nothing.
Sure, some languages might have builtins for comparing certain things, or doing intersections/differences, but those only apply to arrays of primitives, and even in those cases the utility of the builtins completely depends on the use case, and those builtins still have nothing to do with syntax.
artikae · 11h ago
> those only apply to arrays of primitives
I guess you've not written much python, or just not used any custom types in lists if you have.
In this case, the builtins are syntax, namely the `==` operator. There's a uniform syntax for comparing two objects for equality.
cluckindan · 2h ago
Sure, the language has a mechanism for overriding the equality operator for classes, just like Java has .equals(), but the code is overriding the builtin algorithm. The case of comparing mixed type arrays is not a usual case and seems contrived. In JS, you could do the same by extending Array and using that, or implementing a custom .equals() for your objects. I suppose Python is a bit more functional in that respect.
somat · 16h ago
On the subject of javascript template literals.
Do they really not support the equivalent to
let template = 'hello ${name}';
let n1 = template.format({ 'name':'joe' });
let n2 = template.format({ 'name':'bob' });
I am not really a javascript programmer. but I was writing some and wanted to store a bunch of templates then fill them out dynamically and was unable to figure out how to do it with the native javascript template system. So I ended up having to write my own. As it appears to be a huge obvious hole in missing functionality, I have to ask. Am I holding it wrong?
While writing this up I was rereading the mdn page on template literals, perhaps tagged templates would do what I wanted, I don't remember why I did not use them(I think it is still not possible to store the template), but on first glance, yeah, lamentations about the unintuitive syntax and weird features.
cluckindan · 15h ago
Tagged templates probably won’t help, or will be quite messy. But you can just use a thunk with some parameter destructuring:
(Tagged templates won’t help here because the bit in curly braces is an expression which is evaluated to a value first; you’d need some kind of macro system to get access to the original variable name at runtime.)
jvolkman · 21h ago
But if it were Perl, they'd be celebrating.
psychoslave · 22h ago
Awesome, makes me glad I didn't touch Python for years now.
...if you think Python is irregular, what the heck are you using for scripting instead??!
skrebbel · 22h ago
> makes me glad I didn't touch Python for years now.
C'mon, every language has quirks.
jdranczewski · 21h ago
I usually refer to https://pyformat.info/, which doesn't have all this detail, but most of the reasonable stuff is included
cvoss · 15h ago
I think the feature bloat here has passed the break-even point where no one person needs all this, and it takes so long to find what you need in the docs, and you can't be expected to memorize it, because it's so rare to need to use it, and you can't reliably reverse-search the esoteric syntax you encounter, that any given dev is going to take the path of least resistance and write their own routines for this stuff.
Pad left? That's a two-line method at most. Versus trying to remember whether the syntax is x:n< or x:<n or what. It's faster to do it yourself ad-hoc. And if the next dev has a question about how it works, the implementation is right there (and easy to customize!) not buried in the docs and immutable.
zahlman · 12h ago
> Pad left? That's a two-line method at most.
The functionality comes from the prior string.format method, which has been around since Python 2.6 (first released in 2008).
I have found the syntax to be quite mentally "sticky".
> the implementation is right there (and easy to customize!) not buried in the docs and immutable.
There are hooks to customize it.
dvdkon · 14h ago
I'd take the middle road, provide a pad_left/pad_right function or method with keyword arguments for specifying things like the padding character. With how often I need to pad some string (often but not every day), this would be my favourite solution.
The standard library having no solution of its own is how you end up with JavaScript and every project reimplementing their 10% of a proper standard library, only slower and with more bugs.
Though I won't probably use Python's </>/^ format modifiers in my project, I could maybe see them working out in some software that frequently outputs "monospaced" text. In a particular niche, what we think of a needless character-pinching might be seen as a crucial feature and used daily.
al_borland · 15h ago
I find that weird stuff is often used a lot in a single code base, or it’s not used at all. Solve it once, and copy/paste it every other time you need it in the future.
Liftyee · 22h ago
I like this quiz format much more than just reading a doc because I get to guess what it might do before finding out, which reinforces how well I remember it. As a Python amateur I had no idea fstrings were so powerful!
frickit · 22h ago
These games just reinforce that I’m an idiot.
neoden · 22h ago
How can I setup a linter to prohibit stuff like this in my code base?
21/26, with a couple of sleep-deprived brainfarts and a misclick. Learned very little, although I had the wrong reasoning for one of the ones I got right (tested afterward). I don't even use the {var=} thing or walrus operator, though.
I would definitely not do nearly as well on jsdate.wtf. I really still think JS has the greater WTFs.
the_mitsuhiko · 22h ago
I was not sure about the difficulty. Python has some really weird behaviors with some of the custom __format__. For instance f"{'None':<010}" will pad out, but f"{None:<010}" will error. The only one I ended up putting in is the gotcha that bool is an int subclass and gets the int behavior for custom format parameters (which almost sounds like a bug). f'{''''''''''''}' is also a weird one, but that is mostly an issue with the string concatenation syntax and not fstrings :)
There definitely are some more odd / confusing ones.
OJFord · 22h ago
> For instance f"{'None':<010}" will pad out, but f"{None:<010}" will error.
Is that any different than being surprised that 1 + 1 is 2 and '1' + '1' is '11'?
the_mitsuhiko · 22h ago
I don't know, but I find this quite confusing:
>>> f"{'42':<10}"
'42 '
>>> f"{42:<10}"
'42 '
>>> f"{None:<10}"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported format string passed to NoneType.__format__
(Or any other type. What can be padded is quite inconsistent)
davepeck · 7h ago
> What can be padded is quite inconsistent.
Yes, I agree. “What works” is type dependent and Python’s builtin types don’t always behave the same way.
I know you know this, but for those who may not have run across it before and are curious:
f-strings invoke the format() builtin on the evaluated value.
In the case of None:
>>> format(None) # as in f"{None}"
'None'
>>> format(None, '<10') # as in f"{None:<10}"
TypeError: unsupported format string passed to NoneType.__format__
Under the hood, format() turns around and calls type(value).__format__(format_spec). Looking at the docstrings:
>>> help(type(None).__format__)
Help on built-in function __format__:
__format__(self, format_spec, /)
Default object formatter.
Return str(self) if format_spec is empty. Raise Type Error otherwise.
That is, NoneType inherits Python's default object formatter, which doesn't support any format spec; if it's not empty, it's a TypeError.
On the other hand, `str` and `int` both have deep custom __format__ implementations that can do much more, like padding [0].
PS: upcoming t-strings in 3.14 add another twist to format specs: rather than being type dependent, "what works" depends on whatever code processes the Template instance. Unlike with f-strings, format() is not called automatically when t-strings are evaluated; in fact, there's no requirement that processing code does anything with the format specs at all. (Good processing code probably will want to do something!)
That is strange (and would have made a better question I think). Why is it willing to stringify the integer, if not the NoneType?
OJFord · 20h ago
Ah I see, fair enough I suppose. Quite nice for catching bugs though to be honest - since you'll hit an error and realise you need to handle the None case appropriately, rather than a more subtle 'None' appearing somewhere bug.
lionkor · 22h ago
not really, since None stringifies to "None", so you would expect None -> "None" -> padding and whatnot added
istrice · 22h ago
Nice quiz!
I admit I was surprised a couple of times (especially with f"{1<5:1<5}"), many of these features are hardly ever needed
carlhjerpe · 20h ago
I really like the f"{ varname = }" syntax, didn't know about it before.
Fun video, you can really feel the 2012 in it too.
noisy_boy · 18h ago
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.
procaryote · 20h ago
One can question why python needs N built-in ways to printf with different syntax, different ways to escape things and different footguns.
zahlman · 12h ago
> why python needs N built-in ways to [do something where there should ideally be only one obvious way]
It's the same every time: because some people are unsatisfied with the existing ways and want new ones added, but other people will rain fire and brimstone if you remove the old ones.
(BTW: the exact — non-obvious — way the dashes are placed for that line of the Zen is apparently a deliberate joke.)
6thbit · 19h ago
“ There should be one-- and preferably only one --obvious way to do it.” — Zen of Python
pansa2 · 18h ago
> many recent Python extensions seem features in search of use cases […] The real problem with Python, of course, is that its evolution is largely driven by narcissism, not user feedback.
> its evolution is largely driven by narcissism, not user feedback
Let's not psychoanalyze Guido
recursivecaveat · 17h ago
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.
rossant · 12h ago
15/26 despite nearly 20 years of Python. Should I be ashamed? (Granted, I’ve used it a bit less since f-strings became widespread)
jasonm23 · 7h ago
print(f"{f"{{}}"}") - Syntax error... eval it.
elternal_love · 15h ago
Oh am I writing python code for some years this should be fun ... oh. Oh I see. Hmm. Ok, just give it to me: are they turing complete yet or do we need to wait for 3.14?
zahlman · 12h ago
They've been Turing-complete since they were introduced:
I'm surprised to find that there're so many feature of f-string that I've never heard of. I don't think I'm gonna use them any time soon but nice to know about that.
jvdvegt · 21h ago
Why would "0^5" evaluate to 5? (question 21)
And is there a way to link to a specific question?
palotasb · 21h ago
^ is the XOR operator, 0 XOR 5 is 5. (Exponentiation is *)
I made a mistake on 7, but wouldn't have done if I had syntax highlighting.
tomrod · 19h ago
Glorious. I learned a ton, and mostly more than the basics of formatting (something I've never spent a heap of time on). Well done.
CivBase · 22h ago
16/26
I always use a reference when doing anything non-trivial with format strings of any kind and this quiz confirmed I should keep doing that.
Also I've been using Python professionally for over a decade but TIL about the Ellipsis object.
actinium226 · 15h ago
Oof, I got 14/26. I thought I knew f strings fairly well!
underdeserver · 22h ago
Cute quiz! As a reminder, if you're doing anything even slightly complicated with f-strings (beyond :x or var=), leave a comment.
elteto · 22h ago
Hey, I’m doing complicated things with f-strings so I’m leaving a comment as requested.
Disposal8433 · 22h ago
As someone who works on a codebase with almost no comments, please leave a comment for every block of code that does something and explain why (1 comment for every 5 or 10 lines of code should be fine).
Please include any technical and especially business reasons in the ”why”, please.
zahlman · 22h ago
Ohg abg orgjrra gur oenprf! ;)
meinersbur · 22h ago
Just learned about the ellipsis statement and the !a modifier (I already knew !r and !s).
serf · 22h ago
the reward for 100% should be a directory of languages that deals with strings in a sane/singular/readable way.
it's cool that half of those features are there. it's not cool that half the devs that read the thing after creation are going to have to look up the f string features.
Y_Y · 20h ago
I've compiled a directory of those lamguages here:
travisgriggs · 19h ago
Will there be a companion Tstrings.wtf at some point?
davepeck · 15h ago
Hah!
T-strings use the exact same syntax as f‑strings, but their “format spec” (the part after the optional :, like .2f) can effectively be anything.
That might make creating a quiz tricky? With f‑strings, Python immediately calls format() on that spec; with t‑strings, it's simply captured in Interpolation.format_spec. It's up to your code to decide whether to call format() or do something entirely different with this value.
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.
cute pun but compared to busybodies it really hides the implication (i thought you were talking about people with adhd at first).
It was a pretty cool system (on the surface, not having used it, I liked what I saw), but they pulled it straight out. They felt the direction was unworkable. Pretty interesting considering I think the demand for such a facility (as mentioned, once tasted, hard to let cider), plus the work involved to date. Interesting they found it irredeemable to shelve it entirely and back to the white board.
Every time I need to format a number or a date in C#, I just hit the documentation. I refuse to memorize its terrible formatting mini language.
I've never found it difficult to keep them in check. I still can't fathom a use case for nesting f-strings within an f-string substitution, for example. And probably the only format specifier I commonly use is `:02x`.
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.
The key is "identifiers, not expressions."
It would be perfectly possible to add field access without needing to support arbitrary expressions. However, as someone coming from a C# background, I would very much prefer arbitrary expressions.
What does this have to do with either topic?
We'd like exhaustive optimisation, in fact that can't be done and today we're used to a relatively aggressive optimisation which would not have been possible fifty years ago, but we're nowhere close to optimal for non-trivial programs.
Or correctness, it seems as though global static analysis should be possible, at least for something modest like a typical Unix utility - nope, that is known to be mathematically impossible for even quite modest software, only local analysis is at least plausible and its effects are much more limited than we might hope.
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-...
[1]: https://docs.python.org/2.7/library/stdtypes.html#string-for...
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.
Yes: https://docs.python.org/3/whatsnew/3.12.html#whatsnew312-pep...
The = support was added in Python 3.8: https://docs.python.org/3/whatsnew/3.8.html#f-strings-suppor...
I'd prefer writing C# if I had the Linux interaction libs Python has. I'm too dumb to write syscall wrappers
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".
Said as a curmudgeon that has never used a walrus.
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.
print("foo", foo)
I run into something like this all the time. Not many times per program, and I don't actually use the walrus all that often, but when it's the right thing it's so very nice.
But the walrus is good even in the absence of a series of matches. It is quite common to need to know more than the boolean "did this match or not?", and so to my eye it is much cleaner to have one line doing the test and the next lines using the results. I don't care about saving characters or even lines. `m = ...match...` immediately followed by `if m:` feels like overhead for the sake of the programming language's limitations; it's extraneous to what I want to express.
Also, I assert that the pattern is better for correctness. It's like Rust's `if let Some(foo) = f() { ... }` pattern: guard on a certain condition, then use the results if that condition is true.
invites injecting code in between the assignment and the test. They're two different things to the reader. And it's not as clear that `v` should only be used in the consequent body. says exactly what it means: `f()` may return a truthy value. If it does, do something with it.I feel the same way with rust control flow being used as an expression, and am disappointed when other languages don’t have it.
Even though one could argue it makes things more complicated, I feel more mental freedom and feel the language getting out of the way.
The assignment is the least problematic thing on that code. I do even disagree on it being a problem.
Edit: someone downvoted me because they don't understand there is no walrus operator here
Glad this is nowhere near Wat [2], though.
[1]: https://ezhik.jp/f-string.lua/
[2]: https://www.destroyallsoftware.com/talks/wat
A smaller mistake that I find nonetheless amusing is that he uses Array(16) hoping to get 16 string separators. Oops, off by one error :)
Not saying I agree, but was definitely expecting that to be the main topic of discussion here...
What does an algorithmic task such as array comparison have to do with language syntax? The answer is nothing.
Sure, some languages might have builtins for comparing certain things, or doing intersections/differences, but those only apply to arrays of primitives, and even in those cases the utility of the builtins completely depends on the use case, and those builtins still have nothing to do with syntax.
I guess you've not written much python, or just not used any custom types in lists if you have.
In this case, the builtins are syntax, namely the `==` operator. There's a uniform syntax for comparing two objects for equality.Do they really not support the equivalent to
I am not really a javascript programmer. but I was writing some and wanted to store a bunch of templates then fill them out dynamically and was unable to figure out how to do it with the native javascript template system. So I ended up having to write my own. As it appears to be a huge obvious hole in missing functionality, I have to ask. Am I holding it wrong?While writing this up I was rereading the mdn page on template literals, perhaps tagged templates would do what I wanted, I don't remember why I did not use them(I think it is still not possible to store the template), but on first glance, yeah, lamentations about the unintuitive syntax and weird features.
Hell is paved with good will I guess. Probably Justine should update https://justine.lol/lex/
C'mon, every language has quirks.
Pad left? That's a two-line method at most. Versus trying to remember whether the syntax is x:n< or x:<n or what. It's faster to do it yourself ad-hoc. And if the next dev has a question about how it works, the implementation is right there (and easy to customize!) not buried in the docs and immutable.
The functionality comes from the prior string.format method, which has been around since Python 2.6 (first released in 2008).
https://docs.python.org/2.6/library/string.html#formatspec
> It's faster to do it yourself ad-hoc.
I have found the syntax to be quite mentally "sticky".
> the implementation is right there (and easy to customize!) not buried in the docs and immutable.
There are hooks to customize it.
The standard library having no solution of its own is how you end up with JavaScript and every project reimplementing their 10% of a proper standard library, only slower and with more bugs.
Though I won't probably use Python's </>/^ format modifiers in my project, I could maybe see them working out in some software that frequently outputs "monospaced" text. In a particular niche, what we think of a needless character-pinching might be seen as a crucial feature and used daily.
I would definitely not do nearly as well on jsdate.wtf. I really still think JS has the greater WTFs.
There definitely are some more odd / confusing ones.
Is that any different than being surprised that 1 + 1 is 2 and '1' + '1' is '11'?
Yes, I agree. “What works” is type dependent and Python’s builtin types don’t always behave the same way.
I know you know this, but for those who may not have run across it before and are curious:
f-strings invoke the format() builtin on the evaluated value.
In the case of None:
Under the hood, format() turns around and calls type(value).__format__(format_spec). Looking at the docstrings: That is, NoneType inherits Python's default object formatter, which doesn't support any format spec; if it's not empty, it's a TypeError.On the other hand, `str` and `int` both have deep custom __format__ implementations that can do much more, like padding [0].
PS: upcoming t-strings in 3.14 add another twist to format specs: rather than being type dependent, "what works" depends on whatever code processes the Template instance. Unlike with f-strings, format() is not called automatically when t-strings are evaluated; in fact, there's no requirement that processing code does anything with the format specs at all. (Good processing code probably will want to do something!)
[0] https://docs.python.org/3/library/string.html#format-specifi...
I have so much Python to learn, I scored 10/26
[1] https://www.destroyallsoftware.com/talks/wat
It's the same every time: because some people are unsatisfied with the existing ways and want new ones added, but other people will rain fire and brimstone if you remove the old ones.
(BTW: the exact — non-obvious — way the dashes are placed for that line of the Zen is apparently a deliberate joke.)
Mark Lutz
https://learning-python.com/python-changes-2014-plus.html
Let's not psychoanalyze Guido
I'm surprised to find that there're so many feature of f-string that I've never heard of. I don't think I'm gonna use them any time soon but nice to know about that.
And is there a way to link to a specific question?
https://docs.python.org/3/library/operator.html#mapping-oper...
I always use a reference when doing anything non-trivial with format strings of any kind and this quiz confirmed I should keep doing that.
Also I've been using Python professionally for over a decade but TIL about the Ellipsis object.
And for regex you have this in some languages: https://docs.python.org/3/library/re.html#re.X
it's cool that half of those features are there. it's not cool that half the devs that read the thing after creation are going to have to look up the f string features.
T-strings use the exact same syntax as f‑strings, but their “format spec” (the part after the optional :, like .2f) can effectively be anything.
That might make creating a quiz tricky? With f‑strings, Python immediately calls format() on that spec; with t‑strings, it's simply captured in Interpolation.format_spec. It's up to your code to decide whether to call format() or do something entirely different with this value.