Hacker News new | past | comments | ask | show | jobs | submit login

If anyone's looking for some more good WTFs in Python: https://github.com/satwikkansal/wtfpython



I must be a crusty Python programmer, because none of the first few examples seemed like WTFs to me at all. I think a WTF is when you think you know a language and then see something that behaves completely unlike how you expect.


I was annoyed by the number of RTFMs they call WTFs. They even claim some WTFs where Python behaves exactly as a naive user would expect, e.g. if I put 5 and 5.0 in a dict they are treated as the same key.

I saw two valid ones: "Lossy zip of iterators" is because Python conflates iterables and iterators, "The disappearing variable from outer scope" where deleting the exception is contrary to how scope works everywhere else.

They also miss the classic newbie head-scratcher:

    x = []
    for i in range(10):
        x.append(lambda: i)
    x[0]()


I'd also classify WTFs as something that behaves completely unlike how you'd expect based on knowing many other similar/related languages, also.


After seeing years / decade of such things about PHP, welcome to our world.


Wow.

(a) I don't know Python as well as I thought I did. (b) I suddenly never want to use it again.

All these edge cases! All these behaviors, which I'm sure were added with the noble intention of increasing developer convenience, but which I'm equally sure have cost a larger amount of developer sanity!


I've been writing python for about a year and change now (~7 years of engineering generally) and I never want to use it again.

It feels to me like javascript in that it's "popular" because people already use/know it. So that huge existing codebase is the equivalent to the web; if you want to build on it, you're stuck with this.

But my lord. Whitespace sensitivity is a terrible choice and there are piles of kludges trying to work around that.

It means no multi-line lambdas so you end up with these unreadable list comprehensions `[ sub_item.value for sub_item in item.sub_items in items if item.is_the_best ]`. Lines copied into the console care about indentation which is definitely not a fun DX.

Not to mention these random global functions everywhere. Whew.

Mistakes were made.


> Whitespace sensitivity is a terrible choice

It's my favourite feature of Python and I actively seek out other languages that make this terrible choice.

I regularly use Python and a couple of curly brace languages and coming back to Python always feels like a breath of fresh air.


> Python always feels like a breath of fresh air.

That’s a good way to put it. Significant indentation makes the syntax so much more lightweight.

This image [0] is supposed to be a joke, but to me it clearly demonstrates how braces and semicolons are both ugly and redundant.

[0] https://www.reddit.com/r/ProgrammerHumor/comments/2wrxyt/


> > Whitespace sensitivity is a terrible choice

> It's my favourite feature of Python

It's like the speed bump: It's for people who can't follow simple rules. It inconveniences you, but is rationalized with that it ultimately makes the world a little safer for everyone, including you.

Me, I unindent temporary code like debug-print statements and literals overriding actual data. This makes it impossible to commit by accident.

But my argument against whitespace sensitivity would be that bad and unreadable code is bad and unreadable regardless of its whitespace. In fact, force-formatting it just hides the evidence.


> It means no multi-line lambdas so you end up with these unreadable list comprehensions `[ sub_item.value for sub_item in item.sub_items in items if item.is_the_best ]`

How would you rather write that code? Is it lack of multi-line lambdas that stops you from writing it as you would like?


    items
      .select(&:is_the_best)
      .flat_map { |i| i.sub_item.map(&:value) }
Not having functional constructs really hurts readability, and I don't want to think about what would happen to the list comprehension with more complicated logic


> It means no multi-line lambdas so you end up with these unreadable list comprehensions

You can have multi-line lambdas, just use parens:

    (lambda: look.ma.im
        .on_two_lines)
And, yeah, if it's complex enough that it should have control flow, you just write a nested function.

And your example is more clearly expressed as a plain loop:

    new_list = []
    for item in items:
        for sub_item in item.sub_items:
            if item.is_the_best:
                new_list.append(item)
I don't like the comprehension syntax. The correct comprehension is:

    [item
     for item in items
         for sub_item in item.sub_items
             if item.is_the_best]
Hopefully that makes plain what they were going for, but in practice, list comprehensions are often a contradiction in terms.

> Not to mention these random global functions everywhere.

We ought to be able to have it both ways, it should be possible to lift a closure and treat the variables it closes over as arguments, but I wind up doing that manually just so I can test them directly.

> Lines copied into the console care about indentation which is definitely not a fun DX.

I think the issue with breaking copy and paste is the real valid complaint against indentation-sensitive languages. The tooling just isn't there, and this continues to be the case after 20 years.


Not allowing multi-line lambdas is beneficial for code readability. If your lambda needs multiple lines, you should write a function.


Chained promises are much easier to read when the steps are inlined as multi-line lambdas rather than having to read the code backwards for every function definition. And as much as i love list comprehensions, map() and filter() with 2-3 line lambdas also make a lot of sense.

Sure, you "should" use asyncio instead of promises but honestly it has its own problems, mostly in that it requires the rest of your legacy code base to also be async.


If you don't want to use a language because in theoretical scenarios it's possible construct unintuitive operations with it, then what language are you left with?


I was going to bring up Haskell as a language with clear semantics, but even that has its quirks, like

    Prelude> [1, 3 .. 10] :: [Float]
    [1.0,3.0,5.0,7.0,9.0,11.0]
In Haskell, this is syntactic sugar for the function enumFromThenTo (in typeclass Enum), which in my view should not have a special case implementation for Float.


LISP?


>>> row = [""] * 3 #row i['', '', '']

>>> board = [row] * 3

>>> board[0][0] = "X"

>>> board

[['X', '', ''], ['X', '', ''], ['X', '', '']]

I almost felt helpless for an hour when I used this list initialization [0] the first time in my code and couldn't find the reason why my unit tests where failing.

[0] https://github.com/satwikkansal/wtfpython#-a-tic-tac-toe-whe...


Whooaaa! This is a gem and should be a handy reference to look up.

I knew about the hash function, and how it generates same hash value for objects that have same numerical value. But it's so easy to forget!


Wow, some of those entries are painful to read.


That thing is good, it's just a bit of a pitty the titles are so undescriptive. For example a typical one (in other languages as well) like a closed-over loop variable having the value of the last iteration in the closure, is called "the sticky output function". Meaning if this weren't all on one page it would be hard if not impossible to find what you're looking for.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: