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'?
>>> 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)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!)
[0] https://docs.python.org/3/library/string.html#format-specifi...
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).
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.
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.
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...
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`.
Isn't that mostly just a side effect of using the proper full python parser for F-strings? The syntax for what was allowed inside an F-string kept getting more and more permissive, until they just decided to permit ~all of python inside an F-string.
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.
> 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.
If you want to do more complex "Dynamic SQL", say you are writing a query builder where people can fill out fields to do a complex query, your best bet is JooQ, which I use heavily at work.
Not saying I agree, but was definitely expecting that to be the main topic of discussion here...
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.
const tpl = ({ name }) =>
`hello ${name}`;
const n1 = tpl({ name: "joe" });
const n2 = tpl({ name: "bob" });
Or if you don’t like the duplication of property names, you could do it without destructuring: const tpl = (o) =>
`hello ${o.name}`;
Look for the free online book ”You don’t know JS”, it is good reading. const template = ({name}) => `hello ${name}`;
const n1 = template({ name: 'joe' });
(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.)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.
class Thing:
def __init__(self, a, b):
self.a = a; self.b = b
def __eq__(me, them):
return me.a == them.a and me.b == them.b
>>>[1, 2, Thing(6, "Hi")] == [1, 2, Thing(6, "Hi")]
True
>>>[1, 2, Thing(6, "Hi")] == [1, 2, Thing(6, "Hello")]
False
In this case, the builtins are syntax, namely the `==` operator. There's a uniform syntax for comparing two objects for equality.Hell is paved with good will I guess. Probably Justine should update https://justine.lol/lex/
C'mon, every language has quirks.
If you look at Justine post, Ruby is given as the hardest to make highlighted correctly. Much of it I guess however pertains to the many ways it's possible to switch to in place string feeding. It's harder to deal with for automated parsers, but it's generally not pushing human writers to encode in a way that is later harder to interpret by fellow humans with many surprising pitfalls to keep in mind. It doesn't mean that Ruby doesn't have any pitfall, but at least its community is using this low level of suprise as a guiding principle.
Optimizing for the shortest encoding string is missing the point that the biggest cost will be how much time some human being will need to understand correctly what was the intended result. Python used to emphase easy to read over syntax tersrness golf. Looks like it changed.
Now, they are people who love that kind of golf, I'm sure. So for them, that's certainly good that Python is digging that rabbit hole.
It's ok not everyone share the same taste, isn't it?
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.
>>> '{:{}{}}'.format('m', 'o<2', 3)
'moooooooooooooooooooooo'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.
>>> foo='bar'; print(f"{foo=}")
foo='bar'
Wow, never knew you could do that.print("foo", foo)
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.
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 reason is that focusing on formatting CPU performance misses a bigger issue: memory thrashing.
There are a surprisingly large number of places in the average log-happy program where the arguments to the logger format string can be unexpectedly large in uncommon-but-not-astronomically-improbable situations.
When the amount of memory needed to construct the string to feed down into the logger (even if nothing is done with it due to log levels) is an order of magnitude or two bigger than the usual less-than-1kb log line, the performance cost of allocating and freeing that memory can be surprising—surprising enough to go from “only tight number crunching loops will notice overhead from logger formatting” to “a 5krps fast webhook HTTP handler that calls logger.debug a few times with the request payload just got 50% slower due to malloc thrashing”.
Bottom line, still a micro optimization on a modern machine in the vast majority of cases.
If you have a truly costly format op and still want to use fstring, look into .isEnabledFor().
And is there a way to link to a specific question?
https://docs.python.org/3/library/operator.html#mapping-oper...
Mark Lutz
Let's not psychoanalyze Guido
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.)
I have so much Python to learn, I scored 10/26
Glad this is nowhere near Wat [2], though.
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 :)
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.
>>> 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.Edit: someone downvoted me because they don't understand there is no walrus operator here
print(f"{a:=10}") >>> print(f"{(a:=10)}") 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 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.
The assignment is the least problematic thing on that code. I do even disagree on it being a problem.
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.
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. 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:
...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.:= is so common in other languages, or even other Python statements, that it doesn't bother me at all, but Python is such an imperative-first language that its presence is rarely felt.
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.
$ py3.6
>>> def arbitrary_code(): pass
...
>>> f"{exec('arbitrary_code()')}"
'None'
>>>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 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.
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.
underdeserver•6mo ago
zahlman•6mo ago
Disposal8433•6mo ago
And for regex you have this in some languages: https://docs.python.org/3/library/re.html#re.X
cluckindan•6mo ago
elteto•6mo ago