14 ms·
WTF Python: Exploring and understanding Python through surprising snippets
- Maakuth 6y agoAh, brings to my mind the classic talk: https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- th0ma5 6y agoThat one was all cynicism that baited the experienced and confused and discouraged beginners... this one actually takes the important next step of making it all teachable which is really great.
- eyelidlessness 6y agoOh come on Gary literally runs a top notch teaching site and the link was a bit of fun on his previous teaching site.
- th0ma5 6y agoWell he also started a spread of snark that made the entire industry look bad. His own site is called destroy all software. Even if that is meant to be ironic, even that irony isn't helpful.
- eyelidlessness 6y agoI sincerely think you’re taking Gary and the software industry too seriously. Wat is nowhere near as reputation damaging as, say, “PHP a fractal of bad design”, and PHP is doing great. People have aped the talk to highlight rough corners in their own preferred ecosystems. It’s fun. Programming is dealing with a lot of broken stuff. Embrace it and have a laugh.
- th0ma5 6y agoI did that day I first saw it, and then after three years of people pointing and laughing at how awful computers are for upvotes and likes it stopped being funny.
- eyelidlessness 6y agoThey’re not laughing at you, in case you need to hear that. And it’s not computers that they’re laughing at either. It’s human mistakes, misjudgments, and so on. I’m exceptionally talented as a software developer and I make absolutely boneheaded decision, designs and mistakes sometimes. I find it comforting when I just accept my imperfections. I’m more likely to attach an everythingisterrible.gif to my own PR than anything else. It’s made me feel more at ease with myself and my craft to lighten up and have a casual cynicism about the whole thing. Computers aren’t bad but most of what happens on them is. And that’s okay. Most of everything is bad, in varying degrees. At least the silly bad stuff brings a smile to people’s faces.
- th0ma5 6y agoNo I totally get this. But the cynicism has been relentless since this online, and contagious, and many use it as an in joke, and this is serious stuff. People's careers... 6 hour conference calls involving tens of experts trying to get to the bottom of these things. Most of these things are just people doing things outside of the language spec and then are somehow surprised when it doesn't work and then make a joke of it all and I don't see how that helps anything. Again, it was funny at the beginning, but now I really wonder if these people just want to hurt other people by being abusive with this incessant snark. Perhaps this an Eternal September issue, so maybe you're somewhat new, but shitting on things is easy.
- serial_dev 6y agoOh no, someone was being funny! Think of the beginners, they can't deal with that! The wat talk is funny and educational. I watched it with just a couple of weeks of experience in JavaScript, and I learned a lot from it. There are other great resources (the good parts, the you don't know js series, etc), but none of them offer value so quickly. The wat talk is a great reminder to learn more about the language you are using and start reading the aforementioned books.
- th0ma5 6y agoOh no, more sarcasm. If only I was in on the joke /s
- eyelidlessness 6y agoI already had the song NaNNaNNaNNaN watman in my head before I clicked the link. Edit: okay this thing that gave me a little smile deserved a downvote for some reason I guess. Hope whoever else comes along and doesn’t like a cute fun thing or me enjoying it can explain why.
- Maakuth 6y agoYou do know what they say about Watman - he's not the hero we deserved but the hero we need. Have a nice day!
- Groxx 6y agoThis is a really impressive list of oddities, with solid explanations on every single one. Excellent resource, thanks!
- a-dub 6y agoall these cool new features! what's the thing they always say about python? "there's more than one way to do it"?
- Groxx 6y agoOur old friend, Tim Toady. He has never left us, even though Python did try to pour bicarbonate on him.
- dagurp 6y agoThat's a Perl slogan but I suspect you already knew that
- cutler 6y agoPython was always a second-rate imitation of Perl. Camel > Python.
- a-dub 6y agoyep, corresponding python slogan from PEP-20: "There should be one-- and preferably only one --obvious way to do it." this was the compelling argument for python to those who were struggling with large and hard to maintain legacy perl codebases. python was supposed to be a constrained, simple language that encouraged readability- ideally a solution to the problems everyone was facing. history really does appear to repeat it seems, even in computerdom where the timescales are shorter.
- alpaca128 6y agoAh yes, the one about mutable default arguments was a real surprise for me once in a debugging session. Python only initialising them once during the whole script runtime is not exactly the behaviour anyone would expect.
- klodolph 6y agoThey're also an escape hatch, def main(): funcs = [] for x in range(10) def f(x=x): return x funcs.append(f) print([f(x) for f in funcs]) The "x=x" default argument is necessary here. In this example it's just a constant but this trick is often used with mutable defaults.
- quietbritishjim 6y agoYou're talking about something different. The parent comment is about mutable default arguments are shared between all calls to that function [1], but you seem to be talking about functions defined in for loops all sharing a reference to the same variable [2]. [1] https://github.com/satwikkansal/wtfpython#-beware-of-default-mutable-arguments https://github.com/satwikkansal/wtfpython#-beware-of-default... [2] https://github.com/satwikkansal/wtfpython#-loop-variables-leaking-out https://github.com/satwikkansal/wtfpython#-loop-variables-le... > In this example it's just a constant but this trick is often used with mutable defaults. Your example doesn't work with mutable defaults. Ironically it falls into the trap the parent comment was referring to! def main(): funcs = [] arg = [] for x in range(10) arg.append(x) def f(arg=arg): return arg funcs.append(f) print(*[f() for f in funcs], sep="\n") # result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] printed 10 times These functions will all return the same 10-element list. Instead you need to use `def f(arg=arg[:])` or `def f(arg=copy(arg))` (after from copy import copy). > print([f(x) for f in funcs]) Just a typo but you meant print([f() for f in funcs])
- tcard 6y agoThese are two manifestations of the same feature: function arguments's default values are evaluated when the function is defined. In one case, the value is a non-mutable int, while in the other it's a mutable list, but in both cases each call to those functions will share the same value for that argument (unless overriden).
- Doxin 6y agoI feel like WTFs caused by doing something deeply weird hardly counts against the language. Yes doing the walrus operator inside brackets works. Yes of course it's going to be doing weird things. Is this really surprising to anyone? It's not what that operator is for. Yes comparing strings with "is" works sometimes. It's not checking equality and it isn't supposed to. In another implementation using "is" might work all the time or none of the time. Don't use "is" to compare strings. it's not what that's for. Yes chaining mismatching operations does weird things. Be clearer in how you write your code. It's no surprise that writing confusing code results in confusing results. I've been seeing a growing anti-python sentiment lately, and most of it seems to be based on these sorts of odd perceived language defects. Python is by no means perfect, but none of the code on this page is something anyone would actually write in production code on purpose. I suppose it's a sign of python getting more popular.
- evil-olive 6y ago> I've been seeing a growing anti-python sentiment lately Despite the name, I don't think this is anti-python, in the same way classics like [0] are anti-PHP. The project README mentions this a bit: > While some of the examples you see below may not be WTFs in the truest sense, but they'll reveal some of the interesting parts of Python that you might be unaware of. I find it a nice way to learn the internals of a programming language, and I believe that you'll find it interesting too! 0: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/ https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
- Doxin 6y agoFair enough I suppose, but I'm still seeing posts like the OP getting linked all over the place by people trying to disparage python as a language. Of course once you get more deeply into a language it's probably a really good thing to get to know all the weird edge cases. One of my favorite past-times is writing intentionally terrible code just to exercise some of those edge cases. For instance this hello world code uses quite some oddball language features: https://gist.github.com/SuperDoxin/fde4fdad237e85a90c8764cdf88eadf5 https://gist.github.com/SuperDoxin/fde4fdad237e85a90c8764cdf...
- pansa2 6y ago>>> another_tuple ([1, 2], [3, 4], [5, 6, 1000]) >>> another_tuple[2] += [99, 999] TypeError: 'tuple' object does not support item assignment >>> another_tuple ([1, 2], [3, 4], [5, 6, 1000, 99, 999]) This one's my favorite. Although it's a rare issue that you're unlikely to come across, it's a side-effect of a much bigger design flaw in Python - the decision to make `a += b` behave differently from `a = a + b`.
- maweki 6y agoIt's a design decision to allow it to behave differently, as you can implement += with a more efficient in-place version, instead of a copying one where the lvalue might be different/new and you have to keep the old a. I even think this is good design, as by convention + does not mutate its arguments but += can be a fast mutating version.
- pansa2 6y agoI guess reference counting makes copying a list in Python a much more expensive operation than it would be in other languages. Although perhaps it could also be used to avoid copying in cases where the list's own reference count is exactly 1? Anyway, there's nothing wrong with offering an operator to change a list in-place, but IMO `+=` is the wrong way to spell it.
- maweki 6y ago> but IMO `+=` is the wrong way to spell it. Again, operator overloading. __iadd__ just calls append. Many a library might do something much better. But as it looks like the in-place version of +, even for lists it seems fine (although I've been missing a concat-operator in python for years - seems we're stuck with +).
- yongjik 6y agoI'd say it's even deeper - the += operator is fundamentally tied with Python's semantics. Consider: >>> a = [1, 2] >>> b = a >>> a = a + [3] >>> b [1, 2] It has to be [1, 2] because the third line creates a new object. If a + operation changes its operand, then the concept of object identity breaks down - at least, it will be a very different language.
- tgbugs 6y agoWhile this repo is great for education, it doesn't cover the deep problems with Python that you only discover when you try to do something "unapproved" or slightly off the happy path. Depending on the domain you are working in you may never encounter such a situation, but if you do, be prepared to lose your sanity one omnipresent design flaw at a time. Over the years I've been collecting a series of cases that are somewhat more nihilistic and depressing under the general category of LOL PYTHON. The little inconsistencies, the boilerplate that is required if you don't want to use only the approved, not performant, and breaks all sorts of other hidden assumptions so you can't actually use the code outside very narrow settings approach, eventually just evoke a nihilistic chuckle and a note to never start a new Python project again.
- einpoklum 6y agoI think you forgot the link to your collection?
- tgbugs 6y agoThere are some that you can find in [0], but they are often de-contextualized (and sometimes aren't really Python's problem, because there was a trade off that had to be made, and the LOL is just frustration as a result of the bad ergonomics that it creates), those and some others will eventually go in an longer post when I finally get that set up. To give one concrete example of an LOL PYTHON consider the @property decorator. While it seems like a really cool and handy idea that could let you save some typing in some cases, it is actually a trap that you should almost never use because when you inevitably discover that there is some parametrization that you need to pass to the ... attribute, you are suddenly faced with a massive refactoring. Also if you ever make the mistake of having boolean properties such as isDir or isUrl and somewhere else you have a predicate that is a function instead of a property then isFile will always return True when you meant isFile(). The only safe solution is to never use properties/attributes as predicates. These are nasty footguns that seem obscure and unlikely to affect you until you have the misfortune to already be in the briar patch. 0. https://github.com/search?q=%22LOL+PYTHON%22+user%3Atgbugs&type=code https://github.com/search?q=%22LOL+PYTHON%22+user%3Atgbugs&t...
- j0057 6y agoPython should just remove the `is` operator. If you really need to compare object identities, which is already a rare scenario to begin with, you can explicitly compare the object IDs. Checking if something is or is not True/False/None can just be treating the thing as True-ish or False-ish. Making the language bigger is not nearly as impressive as making it smaller!
- dagw 6y agoI find checking for "is None" very useful, but that is also the only time I use 'is'. I try to avoid checking for True-ish and False-ish whenever possible, preferring to be more explicit.
- shoo 6y agoI agree about explicit None checks. It is often useful to distinguish None from the zero value of a type such as a numeric zero, empty string or empty collection. I also write "is None" & "is not None" everywhere when doing these explicit checks. Yet the same be benefits of explicit None checks can be attained by writing "== None" i.e. doing value based comparisons against a None value. So this argument does not explain why "is" is necessary.
- pansa2 6y agoI think the reason not to write `== None` is that classes can override `==`: class C: def __eq__(self, other): return True C() == None # True C() is None # False
- deleted 6y ago[deleted]
- davesque 6y agoOh look. Another techie trying to gain cred by hating on things.
- gspr 6y agoThe Python/NumPy gotcha that has caused me the most pain by far is def foo(x, y): return x+y where the intention is for x and y to be NumPy arrays, but the caller accidentally passes lists (or vice versa). This is especially common because a lot of NumPy functions are agnostic as to whether they operate on arrays or other iterables.
- avian 6y agoI've been bitten by this as well. In general, having two types that are mostly, but not always, interchangeable, often leads to hard to debug problems. A similar situation that comes to mind was str and unicode types in Python 2. Sometimes you end up with a stray value of a wrong type somewhere in a complicated data structure. It gets propagated down the line since most parts don't care if it's one type or the other. This makes it possible for the value to propagate far away from the original mistake that caused it to be of the wrong type in the first place. Finally when some code chokes up on it, it can be completely unrelated to the code that needs fixing. Since it's hard to debug the original cause this is often fixed by just adding the type conversion at the place that choked up. But this is just kicking the can down the street, since the remaining type inconsistency will soon pop up a problem somewhere else.
- nabla9 6y agoThat's clear example of bad language design. Overloading operator syntax and breaking semantics in dynamic language is big no-no. It may not be good for statically typed languages either, but at least they can get away with it. Convention of using + for joining sequences and addition is human language level abstraction. It breaks in programming. In formal theory strings over an alphabet form a semiring, but (*) is used for concatenation and (+) for alternation. Not applicable anyway. Last, no clear syntactical cue to distinguish operators acting on elements from those acting on objects.
- antpls 6y agoI disagree. The intention in foo is not for x and y to be numpy arrays, it is to add 2 objects together. "If it walks like a duck and it quacks like a duck, then it must be a duck". That function can work on anything implementing +, this is the philosophy of Python. It enables the whole numpy, dask, TensorFlow ecosystem. If you intend to limit it to numpy arrays, you either use numpy 1.20's static types, or you add more checks at the beginning of the function, such as np.asarray(x).
- bdg 6y agoTo be frank I think the python community lacks self-awareness. There's this ritual you have to do when learning python: blindly accept status-quo design choices as "universally good" (but call it "pythonic"), and crap on other languages. Never admit python or the ecosystem has issues or shortcomings, and if another language has something better than yours (eg, Yarn, or Composer is way better than anything in Python (poetry is a good step but not a competitor)), or offer really good IoC tools: just double-down on the status-quo and say something like "looks over engineered, I see no reason why I would need that". Pythonic is putting a decorator everywhere, coupling the system together into a ball of mud where you can't decouple it. Pythonic is having annoying mixed naming conventions. Why do I need methods to have underscores in 2021, but classes to use camel case? Why do my lines of code need to fit on a punched card invented decades before the language was invented? Why does the idiomatic "one true way" need to be what some other engineer thinks is "the one true" way? Python is a fractal of closed-mindedness and single-use code. There's a reason it took a decade to get projects onto python 3, but PHP can swiftly move the entire ecosystem from v5 to v7 in a year. FastAPI is getting a lot of praise recently, but frameworks of this style are a dime-a-dozen in other languages and have been around for a decade. Pythonic is a meaningless word that just gives firepower to the most confident-sounding engineer in a company to shoot down anyone who doesn't code like them. It's not an objective concept, it's innovation poison.
- AyrtonB 6y ago> However, know when to be inconsistent -- sometimes style guide recommendations just aren't applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best.
- bdg 6y agoMy issue is with the behaviour of humans who belong to this hegemonic cult, not the document.
- czardoz 6y ago> Why do I need methods to have underscores in 2021, but classes to use camel case? Why do my lines of code need to fit on a punched card invented decades before the language was invented? Why does the idiomatic "one true way" need to be what some other engineer thinks is "the one true" way? These problems are applicable to all languages, not just Python.
- pachico 6y agoIf this article was about the WTFs in PHP there would already be hundreds of comments saying how much it sucks. You get less criticism about other languages although they also have bad design decisions (which is totally fine since nothing is perfect). I wish these things weren't so biased by the emotional investment people put in working tools, which is all a language is.
- trfarmer 6y agoMy biggest issue with Python that is not related to syntax is that it isolates you from the Unix machine in a bad way. I get that they try to support Windows with the same abstractions, but they are all leaky and subtly broken (like shutil). If you use Python too much, you lose the understanding of Unix. Other languages aren't like that, for example Perl and Go. Whenever I stop using Python for 2 weeks and focus on C or Go, my understanding of the machine and what is actually going on increases dramatically. I've even considered learning Perl, but haven't done it yet due to lack of time.
- cutler 6y agoThere's always Ruby - best bits of Perl, Lisp and Smalltalk whilst retaining UNIX heritage.
- memming 6y agoTIH python
- fireattack 6y agoCompared to wtfjs, I'd say this is pretty tame. The only one I actually encountered myself is `def some_func(default_arg=[]):` one, but it was indeed a very "WTF" moment.
- wiredfool 6y agoI’ve seen this in large (open source) code bases. The only one that has really bitten me was implicit string concatenation when forgetting a comma in a list literal.
- matsemann 6y agoTips on learning python as a seasoned dev? I will start a new job where everything is done in Python in a few weeks. Learning the syntax of the language is easy, but what about "everything else" using a language entails? Like how is stuff written in practice (pythonic / idiomatic), build tools, libs everyone use, workflow, etc
- wlamason 6y agoI'm not a python developer by trade, but here are some of the resources I have found helpful. The links below are in the order I would read them if I was starting again. Hitchhiker's Guide to Python - https://docs.python-guide.org/ https://docs.python-guide.org/ * Setting up virtual environments * Structuring projects * Code style (PEP8) * Popular libraries for various use cases (i.e. Django/Flask for web applications) setup.py (for humans) - https://github.com/navdeep-G/setup.py https://github.com/navdeep-G/setup.py * Instructs on how to write a setup.py file for distributing python packages * Contains links to other resources to learn more about setup.py Python 3 in One Picture - http://coodict.github.io/python3-in-one-pic/ http://coodict.github.io/python3-in-one-pic/ * I sometimes forget syntax when switching between different languages :) A Guide to Python's Magic Methods - https://rszalski.github.io/magicmethods/ https://rszalski.github.io/magicmethods/ * The special methods typically used with object oriented python that start and end with two underscores "__" Awesome Python - https://github.com/vinta/awesome-python https://github.com/vinta/awesome-python * In their own words "A curated list of awesome Python frameworks, libraries, software and resources." * Whenever I start on a new language or skill, I always check if there is an "awesome" list available Hope you find these as useful as I have!
- matsemann 6y agoWill take a look, thanks!
- CivBase 6y ago>>> a := "wtf_walrus" File "<stdin>", line 1 a := "wtf_walrus" ^ SyntaxError: invalid syntax Why doesn't this work? Isn't the walrus operator effectively just an alternative to the assignment operator which also returns the assigned value? Why specifically make it fail in this situation?
- legobmw99 6y agoFrom the PEP [1] > This rule is included to simplify the choice for the user between an assignment statement and an assignment expression -- there is no syntactic position where both are valid. 1: https://www.python.org/dev/peps/pep-0572/ https://www.python.org/dev/peps/pep-0572/
- CivBase 6y agoAh. I forgot about the "one right way" philosophy. But if there is no syntactic position where both are valid, then why not just add a return value to the existing assignment operator? Surely that wouldn't cause any compatibility issues since prior to its introduction any attempt to use an assignment as part of an expression would raise a syntax error.
- rurban 6y agoLooks like in C++, where the maintainers don't dare to say no to add simple syntax to win popularity contests, but hereby completely destroy the language. JavaScript also comes to my mind. walrus, haha
- coldtea 6y ago# Python version 3.8+ >>> a = 6, 9 >>> a (6, 9) >>> (a := 6, 9) (6, 9) >>> a 6 >>> a, b = 6, 9 # Typical unpacking >>> a, b (6, 9) >>> (a, b = 16, 19) # Oops File "<stdin>", line 1 (a, b = 6, 9) ^ SyntaxError: invalid syntax >>> (a, b := 16, 19) # This prints out a weird 3-tuple (6, 16, 19) >>> a # a is still unchanged? 6 >>> b 16 In what way is ANY of this unexpected? For starters, unpacking is not the same as assigning inside a tuple... (And how is the 3-tuple "weird"? It prints exactly what you did...)