37 comments

[ 2.7 ms ] story [ 72.9 ms ] thread
(comment deleted)
Haha these aren't even wtfs! the id changes or not? oh noooo, people use the id() function so much, I've literally seen it 0 times ever in production code. This is nothing like the JS wtfs where there is no way to predict what it will do, and it violates it's own patterns.

edit: to be more clear, I have run into a lot of JS wtf's trying to write code for work. you just run into bugs all the time where JS doesn't do anything like what would be the obvious thing to do, or the thing that works 99.99% of the time fails depending on the value of the string being 'magic'. with Python I rarely if ever learn some bizarre arcane thing accidentally because it almost always behaves as expected. If you think there's an equivalence here you are just a JS fanboy, JS was a language that evolved largely because of hacks vendors put in and those insane hacks got standardized because it was too late, despite being insane. Python was specifically and carefully designed to minimize unexpected behavior.

If you use a floating point number as a key in a `dict` the wtf should not be pointing at Python.

These are not wtfs, please.

These examples read like someone who is learning to program and who is confused.

    x is not y
    x is (not y)
How can you confuse these two?!
Some of these just seem to be using Python out of spec and being surprised that implementation details exist, and misunderstanding boolean expressions.
See also: https://github.com/satwikkansal/wtfpython

The "interactive notebook" link goes to the colab page.

There are many earlier threads associated with the markdown version, for example:

https://news.ycombinator.com/item?id=21862073 (2019-12-23, 185 comments)

https://news.ycombinator.com/item?id=26097732 (2021-02-11, 163 comments)

https://news.ycombinator.com/item?id=31566031 (2022-05-31, 143 comments)

https://news.ycombinator.com/item?id=37281692 (2023-08-27, 82 comments)

While we're at it. Notebooks aren't a real programming environment.
Who writes python this way? Good Python code, being such a flexible language, relies on conventions. Never seen the id() function in production code. "Is" is canonically used for "if var is None", not string comparisons. Python has a rich library of string methods, maybe use them?

You're finding bugs because you're using the language like a QA tester instead of the way its intended. If I saw string_var is "some_string" in a PR, I would reject it. Even the triple boolean comparison example should just be separated with an "and".

Read PEP20 and PEP8. A goal of Python is to be as readable as possible, not to conform to some spec. You may not agree with that design decision, but all these examples have an obviously more readable alternative which would work.

Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.

>Who writes python this way?

No one, obviously. This is about understanding how the python interpreter functions.

>Python has a rich library of string methods, maybe use them?

How are you able to get the impression that any of this is a prescription on how python programming should be done? Nowhere does it claim to be, in fact if you read the actual content you would understand why these examples aren't the way to accomplish your goals.

Why are you so incredibly defensive when someone points out that a programming language contains weird behavior most people do not understand? This is true for every language. And almost always these examples are given so that people can refine their understanding of the language.

>Jeez. It's like me complaining that Rust looks ugly (it does). The language was designed with a different set of priorities.

The only person complaining is you.

“Is” checks memory location of the values.
The first third of this seems to just be complaining that it's not obvious when two objects are actually the same object, and that if you mistake identity-related operators for other operators then you'll have a bad day.

That's a fair critique. It's a little weird that `is` and friends have dedicated, short, nice syntax.

On the other hand, most compiled languages are compatible with an implementation which admits the following optimization:

  const a: u32 = 42;
  const b: u32 = 42;
  assert(&a == &b);
The language usually doesn't guarantee that different immutable variables have dedicated memory on the stack, or that they even live on the stack in the first place.

That's the same class of error we're seeing here, and even among popular interpreted languages Python is by no means unique. I might get to the bottom 2/3 of the doc later, but the rest was off-putting enough that I don't know I'll bother.

I'm happy to complain about python but I got like a third into it and didn't find any actual wtfs.

Java as contrast has interning wtfs because == between java objects does essentioally what python's `is` does. Python actually made a good choice here and made == do what you'd expect.

Is anyone surprised by `(1 > 0) < 1`? There are languages where it will be rejected because of types, but what else would you expect it do than compare a boolean true to 1?

Is there anything of value later on?

I think there's a few in there. But most people aren't going to write code that way. It's good to know, and keep in your head, but if you have to think too hard in the future if a piece of code is correct, it's probably better to rewrite it anyway. And weird code like that shows up like a sore thumb in reviews.
> Java as contrast has interning wtfs because == between java objects does essentioally what python's `is` does.

It's pretty sensible because i wouldn't expect that operator to call a method - since Java does not have operator overloading.

Yeah, it's not that bad, but `if foo == "bar"` or `foo == Integer(someInt)` looks reasonable, and worse, sometimes works
> Is anyone surprised by `(1 > 0) < 1`?

This is presumably meant as a setup for the fact that the result of `1 > 0 < 1` differs from the result with parentheses set up either way (because this is a chained comparison), and furthermore that such chained comparisons are legal even when they don't have the operators all "pointing" the same way (like in mathematical notation). And that operators like `in` and `is` are allowed to participate in this system on equal footing.

I'm a little flabbergasted at how many comments here boil down to "These aren't WTFs if you understand the language."
Now do a "What the Fuck Excel"
Good point. The "snap to zero" rules are wild in Excel.
I didnt make it past the long list of id() misuses, but its a rookie mistake to confuse referential equality with value equality, and when doing so youre in for a bad time.
The point is not simply about the existence of a separate concept of object identity; it's about the details in the circumstances in which Python creates separate-but-equal objects rather than reusing the same one.
I'm surprised by the reactions to this. It's made immediately clear in the notebook that this is a fun look at some interesting behaviors of Python. There's no implication that there are bugs - I have no idea where anyone got that idea - except for the single actual bug in CPython that is mentioned (https://github.com/python/cpython/issues/54753).

I don't feel strongly about Python, but I do think CPython is a great way for people to learn a bit about how interpreted languages work. The code is quite easy to understand, and you can easily play with these interesting aspects of how languages and runtimes work. Does every Python programmer need to know the difference between `a = 256` and `a = 257`? No. Is it interesting? Yes. Should someone on your team know? Probably.

There's a lot of interesting stuff here. Understanding the difference between the conceptual idea of each line of code and what actually happens is fun, and, in some cases, important.

It probably wasn't a good idea to start it out is showing string constants aren't optimized. If they wanted to talk about really unintuitive reference gotchas initialization in optional function parameters would have been a way better example.
Bad requirements are bugs.

Bugs in understanding. Bugs in good taste.

A roach in your soup is a bug, even if the recipe says it should be there.

a roach in your soup is not a bug if the recipe says it should be there, it's just something you have a preference against.

"You can cook them in numerous ways, from boiling to toasting. An especially delicious cockroach delicacy is said to exist on Madagascar." https://bucketlistjourney.net/edible-bugs-and-insects/

(a roach itself is a bug, different sense of the word bug, but presumably that's not what you are saying)

Aside: In the process of submitting this, I learnt that Hacker News apparently has an anti-censorship title transform built-in. I submitted it with the title taken from the notebook - "What the f*ck Python!" - and HN automatically removed the ! and changed "f*ck" to "Fuck".
I may be wrong, but think a lot of it is undefined behavior. Interpreter may choose to recycle objects or it may not. In case of integers, low enough number in different parts of the program, will be represented by the same object. But if the integer is high enough, different objects will be created. That’s my understanding, but I could be wrong.
Some ways of programming in Python require a LOT of mental effort. And some don't. For example you can do a lot in one line in Python, but I usually have to write extensive comments and references to external docs to keep track of what that one line is doing. I think sometimes it would be easier if I just had to write 3 or 4 lines of self evident code and use that as docs instead.
Great. I enjoy these kind of articles. My all time favorite book for 'C' is Expert C Programming: Deep C Secrets.
Exclamation points are in fact part of the ASCII standard.
My favourite Python misfeature is that `x += y` sometimes does the same thing as `x = x + y` and sometimes doesn't. This leads to minor WTFs like this:

  >>> a = b = (1, 2)        >>> a = b = (1, 2)
  >>> a = a + (3, 4)        >>> a += (3, 4)
  >>> b                     >>> b
  (1, 2)                    (1, 2)

  >>> a = b = [1, 2]        >>> a = b = [1, 2]
  >>> a = a + [3, 4]        >>> a += [3, 4]
  >>> b                     >>> b
  [1, 2]                    [1, 2, 3, 4]
And major ones like this:

  >>> a = ([1, 2], ['one', 'two'])
  >>> a[0] += [3, 4]
  TypeError: 'tuple' object does not support item assignment
  >>> a
  ([1, 2, 3, 4], ['one', 'two'])
The operation raises an exception even though it succeeds!
It’s reassuring that a lot (feels like a majority) of these are noted as resolved or turned into errors that make more sense than confusing behaviors in later editions of the language.

Python 3.5, which is what the WTFs use by default, is relatively ancient.

Hi, author of the repo (https://github.com/satwikkansal/wtfpython) here, pleasantly surprised to wake up to this thread about it. The intention of the project is not to put Python in a bad light relative to other languages, the intention was to explore it more deeply through (sometimes contrived) snippets that give non-obvious outputs. I believe for a regular Python programmer, the "Slippery Slopes" section is a must-know. The first section (Strain your brain), does contain some patterns that you may not ever see in real world, the goal there is to just learn about internals. Agreed some of them are very obvious for a well-learned programmer, and some of them aren't even Python specific, it does help a lot of beginner to intermediate programmers. I remember it being given as a supplementary material in one of the CS-based course.

It is curated with something-for-everyone approach, I expect people to learn a thing or two about Python / CS after going through the snippets. I haven't updated it in a while in a major way, but looking at the feedback I'll try to organise them better, and add new snippets (I have a long personal list that I maintain) in a future release!

Small numbers are "eq" in python. It saves space and time, because probably those numbers dont even exist, and the value is derived from the pointer value.

  >>> a=3
  >>> b=3
  >>> a is b
  True
  >>> a=1e20
  >>> b=1e20
  >>> a is b
  False
But in Commonlisp all numbers are "eq". Very strange. Is there heap of numbers and once created, they reuse those?

  [4]> (setq b 1E20)
  1.0E20
  [5]> (setq a 1E20)
  1.0E20
  [6]> (eq a b)
  T
Holly cow, what a mess. Is the miniscule efficiency gain really worth it especially in such a user friendly language as Python?