13 ms·
WTFPython – Understanding Python through surprising snippets
- NotSammyHagar 7y agoIf only it had curly braces instead of just indention. God, that kills me. And used unicode instead of ascii as the default. And there wasn't python 2.7 vs 3. Someone help me stop this list.
- baq 7y ago1. forget python 2.7 exists 2. can't help with the whitespace, sorry; also take care handling Makefiles
- Erlich_Bachman 7y ago> 1. forget python 2.7 exists But it doesn't work this way. You don't work strictly with the language itself, you work with the whole ecosystem. Libraries, tools, snippets, SO questions, etc. And many of those are still in 2.7 or at least need to specify different solutions for 2.7... It is still a pain.
- protomikron 7y ago> Libraries, tools, snippets, SO questions, etc. And many of those are still in 2.7 or at least need to specify different solutions for 2.7... It is still a pain. Admittedly there were some libraries that took a long time to switch, but at this point there is no serious library left, that did not make the switch.
- dr_zoidberg 7y agoExactly. I've gone farther even, and this is my motto now: Python 3.6+ or burn it to the ground I mself still have to port a few projects yet, but those that got stuck in 2.7-3.5 land are en route to dying a fiery death, and being reborn in 3.6+ (3.8 where applicable, 3.6 as the lowest I'm willing to accept).
- MaulingMonkey 7y agoMaya is still embedding Python 2.x. Soon (tm)... Makefiles are half the reason I enable visible whitespace in my editors.
- NotSammyHagar 7y agoI have worked at places where they insisted on 2 space indention, which made it much much harder to read the code and follow indention levels. It was just stupid of course. That's what really turned me off of python. I know there are bad formatting options for c style languages, and the editor should make it clear. but 2 spaces is too little, too late.
- yoloClin 7y agoI've coded in python for >10 years and C# for ~half of that. I prefer indentation-based control flow purely because it condenses code vertically and creates consistent indentation with actual purpose (many of the devs on C# projects I've dealt with have had inconsistent tab/space settings and no linting). If anything, the one thing I hate about Python3 is dynamic typing - inferring datatypes in function bodies is great but strongly typed (and _enforced_) parameters and return values would clean up a lot of the problems I have seen, and created in the past.
- XuMiao 7y agoPython does have a lot of problems, but it's so far the most flexible language like English. High level programming language is meant to communicate, not to dictate the computation. Python happens to be easy to understand by humans.
- cutler 7y agoRuby rates much higher on the Englishness scale.
- yoloClin 7y agoI don't disagree with anything you've said, but in my opinion strongly typed function definitions/returns are about communication rather than dictation - I'd rather the compiler tell me that I'm passing an unexpected argument type rather than the runtime executor. But to contradict your point, there's also a fair amount of Python which is a dictation - Guido even called his job role 'Benevolent dictator for life'. I really hated some of the guidelines as a new programmer but after dealing with them for some time I understand that consistency is far more important than preference in many/most/all cases.
- GordonS 7y agoHmm, I'm not really a fan of indentation based control flow, but I see the appeal of condensing code vertically. I primarily work with C#, but I've gradually come to like the "K&R"/Javascript style (where the 1st brace is at the end of the first line), precisely because it helps condense code vertically.
- heinrich5991 7y agoUTF-8 is the current default.
- the_mitsuhiko 7y agoIt uses unicode but it doesn’t use UTF-8 by default as external encoding and never as internal encoding except for an optional secondary buffer.
- jwilk 7y agoWhat do you mean by "external encoding", "internal encoding", "secondary buffer"?
- bjoli 7y agoEncoding strings internally as UTF-8 is a bad idea, since you can't do efficient constant time access (utf-8 isn't fixed width).
- heinrich5991 7y agoEncoding strings internally as UTF-8 is a fine idea, usually you don't need constant-time access to individual code points. E.g. PyPy (faster Python, with JIT) does so. Many other languages also save strings as UTF-8.
- bjoli 7y agoAnd then you have to either re-train them to use string cursors or you have lots of people writing inefficient string code.
- heinrich5991 7y agoNot really, no. PyPy demonstrates that.
- BiteCode_dev 7y agoUnicode had been the default for 13 years. 2.7 reaches End of Life in less than 2 weeks. And as for curly braces, try typing: python -c "from __future__ import braces" In a terminal :)
- NotSammyHagar 7y agoThat wasn't what I expected. I thought it would say all is well! Funny.
- bakery2k 7y agoBraces in Python would have issues, e.g. ambiguity with set/dict literals. Most similar languages (except JavaScript) seem to use begin/end instead. Would that be more acceptable than significant indentation?
- Luyt 7y agoWhen you write in a language which uses curly braces for statement grouping, do you indent your code? ...? I thought so.
- michaelcampbell 7y agoGenerally not; emacs or VSCode or IntelliJ or whatever does for me. Does for python, too.
- fxj 7y agoWith the new walrus operator you dont need indents anymore. You can write everything in one line in an array: e.g. y=0 for i in range(10): x=i+1 y=y+x*2 can be written as: [y:=0] + [[x:=i+1, y:y+x*2] for i in range(10)] The square brackets are the new curly brackets.
- joshuamorton 7y agoAs others mentioned, using lambdas this has been possible for ages. There's even a too to auto convert pre walrus code to a single line. It's just that no one does it.
- NotSammyHagar 7y agoThat is evil and wonderful. another reason to love/hate python. I'm working on a project for fun over the holidays and of course I wrote it in python 2.7, and it needs to process unicode so I inflicted double punishment on myself ;-) My work project just happens to use python 2.7, causing pain for everyone.
- odiroot 7y agoThat would have literally made me never look into it in the first place. I would have went with Ruby instead.
- baq 7y agothe first really wtf thing on there is https://github.com/satwikkansal/wtfpython/blob/master/README.md#-mutating-the-immutable https://github.com/satwikkansal/wtfpython/blob/master/README... where both effect and exception happen; up to that point it's business as usual with garden variety corner cases or implementation details.
- avip 7y agoI find the explanation lacking? sort also "changes the list in-place", but you can do ([3, 1, 2],)[0].sort()
- Luyt 7y agoOf course you can. The first element of that tuple is a list, which is mutable, and can be sorted in-place.
- FartyMcFarter 7y agoSo the question is if += and sort are both "in-place", why does one throw and the other doesn't? Clearly "in-place" is not the full explanation here. I think the real explanation is that with += there are two steps: 1- The existing list gets modified. This is fine since lists are mutable. 2- The tuple's reference to the list gets updated (even though this update is unnecessary since the list object's identity is the same). The exception occurs at step 2, but this step is otherwise a no-op. Whether mutating a reference to the same value it already had is an actual mutation is an interesting semantics debate.
- Luyt 7y agoTuples are immutable, but that doesn't mean they can't contain references to mutable objects. When you want to use a tuple as a key in a dictionary, Python checks if all members of the tuple are immutable. If one or more of them aren't, you'll get an 'unhashable type' error.
- avip 7y ago
- deleted 7y ago[deleted]
- mesaframe 7y agoGolden repo. I remember reading this a couple years ago and it opened my eyes to things that I was oblivious to.
- kraf 7y agoMaybe "beautifully designed" is a bit much then In my experience Python is not very nice to work with. I do like the ecosystem for data science though, it's just amazing how much there is. Hopefully the language will grow into something better now that the dictator stepped down.
- FartyMcFarter 7y agoFixings a lot of the WTFs requires breaking backwards compatibility. Given how long it took to transition to Python 3 and how painful the transition was, I'm not sure people will have the appetite or patience for this anytime soon.
- varelaz 7y agoA lot of them worked that way or similar in Python 2 and basically they are not a bugs, but optimization or just language design decision.
- CallocRoast 7y agoSecond this. I can’t understand finding Python beautiful or even elegant. It just took a hodgepodge of features from languages like Haskell and C++, and repackaged them clumsily. It’s good for writing short scripts and throw-away code, but it shouldn’t be used as a serious programming language.
- deleted 7y ago[deleted]
- ezzzzz 7y agoYeah, I'm relatively new to Python as a JVM dev, but am finding myself (accidentally) in the realm of Data Science/Engineering and am looking to Python as opposed to Scala simply due to better Libraries. I do like that Python is really easy to be productive in, but the challenges of scale I guess will be learned as our team gains experience.
- shpongled 7y ago
- jakear 7y agoMy biggest complaint about Python is that it somehow doesn’t get flak for having the same (if not worse) scoping as JS, which gets endless hate for its function-scoped variables. (So much so that block scoped variables are the new normal in JS, but not in Py!) Take for instance: >>> powers_of_x = [lambda x: x^i for i in range(10)] >>> [f(2) for f in powers_of_x] [512, 512, 512, 512, 512, 512, 512, 512, 512, 512] To me this is more absurd than any of the JS “wat”s I’ve seen. In JS: powersOfX = Array.from({length: 10}).map((_,i) => x => x^i) powersOfX.map(f => f(2)) Not a particular fan of the Array.from over list comprehensions in terms of syntax, but I much prefer consistency of semantics JS provides by not adding new syntax. Here Python has special syntax for both list comprehensions and for anonymous single line functions, and they interact in a highly unexpected way. Funny that Py doesn’t need semicolons, as I’m reminded of GJS telling me of adding special forms to languages: “beware the allure of syntactic sugar, lest you bring upon yourself the curse of the semicolon” (paraphrased of course, he said it much better). It seems Python has fallen into the trap of syntactic sugar, with the curse manifesting itself not in the form of a semicolon, but in confusing and unexpected interactions between its various special forms. Another example: the walrus operator and all its oddities listed in TFA.
- varelaz 7y agoThere is nothing bad in general with list comprehension there. Problem is lambda which uses variable from outer scope. In Python, comparing to JS, using variables from outer scope is not so common. I prefer more obvious constructions and in case of problems with scope I can even create class to protect value that I need to manage.
- BiteCode_dev 7y agoI agree totally with that, it's an ugly trap in python, although mitigated by the fact the language doesn't encourage this kind of paradigm so you rarely encounter it. But more absurd than creating a global variable if you forget "var/let/const" ? More absurd than "this" being schizophrenic ? More absurd than having no namespace for 20 years ? That's pushing it.
- aidos 7y ago
- varelaz 7y agoProbably the best collection/explanation of edge cases which are not a bug and supposed to work that way by design that I ever have seen for Python. (it's my pramary language for almost 10 years).
- blackhaz 7y agoIt's interesting to see the walrus operator in there. I wonder, is this another example of unnecessary features being added just to claim a bigger change list, or someone really needs this operator? Why would anyone prefer a walrus instead of adding just one more line to their code, which also helps with readability?
- rhizome31 7y agoThis has been subject to endless discussions already and it's even a factor in Guido's decision to quit his role of BDFL.
- Luyt 7y agoA while ago, I was also sceptical about the walrus operator, although I use assignments in expressions all the time in C++. But when I was perusing a library written Python the other day, I found five spots in which the walrus operator would make sense - eliminating one line of source code without readability suffering from it.
- bakery2k 7y agoIs saving 5 lines of code really worth breaking Python's "one way to do it" design principle?
- pwdisswordfish2 7y agoYes. That principle was quite silly to begin with.
- masklinn 7y agoPython's principle is not an has never been "one way to do it". Python's principle is: > There should be one — and preferably only one — obvious way to do it. Which is a very, very different assertion. And there remains one — and exactly only one — obvious way to do an assignment, because the walrus operator is literally invalid syntax as a statement: >>> [a:=1] [1] >>> a:=1 File "<stdin>", line 1 a:=1 ^ SyntaxError: invalid syntax
- BiteCode_dev 7y agoIt's a good repo, but remember a lot of those are "A Good Thing™". The first snippet is a very good example: a := "wtf_walrus" doesn't work while: (a := "wtf_walrus") works. It's a fantastic design decision. Python took a long time before getting this operator, because it's a language that favors being readable, easy to use, and above all, to learn. But in many other languages, the very same operator is often misused as, or confused for, the operator for equality or assignation. We, as a community, didn't want people to wonder why there are several ways to do assignation (we have this problem with string templating already). Instead, we wanted to be sure that people could ignore the existence of ":=" for some times during their learning process. And so the decision has been taken to make is very easy to distinguish it from "=" and "==", forcing parenthesis when necessary to make it clear this is a completely different use case. Also to hint people at using it only when necessary. If you like scripting in Python because it's so easy to go from an idea to code, it's not random luck. It's because the language is a collections of thousands of such decisions.
- deleted 7y ago[deleted]
- fxj 7y agoin the zen of python (import this) it says: there should be one-- and preferably only one --obvious way to do it. sadly this is not true for a while in python now. Python became a language that can be really hard to read now.
- xscott 7y agoYeah, current versions of Python feel like Guido and the rest changed their minds about what they wanted.
- BiteCode_dev 7y agoPython is a very old language. First version in 1991! This is no surprise Guido evolved with it, and changed his mind about a few things. People are never happy. On the other side of the fence, you have others crying the language is not moving fast enough.
- erezsh 7y agoThis list is a little nit-picky. All the id() equivalencies only exist for optimization, and not out of some promise to the user. The walrus operator can be confusing, but any language can be made to look confusing. It's an advanced feature that should be used very scarcely (if at all). It does point out a few real weak points in Python, but most of them require non-trivial usage of the language. Meanwhile, in Javascript > "a" + undefined "aundefined" Now that's what real WTF looks like.
- rvanlaar 7y agoPython 3 supports typing. The integration with VSCode is also nice. The following is valid typed code and my personal favorite WTF: def func() -> int: return True Because: https://github.com/satwikkansal/wtfpython/blob/master/README.md#-whats-wrong-with-booleans https://github.com/satwikkansal/wtfpython/blob/master/README... I understand the decision that was made in the python 2 era. Nowadays Boolean shouldn't be a subtype of `int`. It goes against the zen of python: - Explicit is better than implicit. It actually happened because I was using the new walrus operator (which I like). def calc(input: int) -> int: return input // 3 def func(input: int) -> Iterator[int]: while input:= calc(input) > 0: yield input *edit: code layout
- d33 7y agoYeah, typing KINDA works until you try to use a library that has no annotations and uses reflections, such as - say - plumbum, boto3 or some other pre-typing shit. It's a bit like with async code - once you decide to use it, you can keep the language but basically need a whole new ecosystem. I tried to write an actual company project with `mypy --strict` passing as a requirement and you quickly end up having abstractions only to bypass mypy, as well as surpress comments as uses of Any. And this is where it pays off to just switch to a statically typedk, compiled language.
- rvanlaar 7y agoYeah, agreeing with what you say. mypy --strict is only useful for projects for which everything is defined. I recently did a project with tornado, which is typed. Typing helped me immensely. My editor would give me type hints, errors and show me other edge cases. For an existing Django project I'm adding types when I touch functions. It helps a bit, but way less. That's also because typing almost forces you to change all the dicts that get's passed around in dataclasses. Which is a lot of work after in an existing project.
- 333c 7y agoFor what it's worth, I like that a bool is an int. It enables concise constructions such as sum(is_true(data) for data in items) to count how many items in an iterable satisfy a condition.
- ngcc_hk 7y agoThis is crazy.
- gonational 7y agoI went to the page thinking, “I’m sure these will be the usual gotchas.” OMG was I wrong... Some of the examples are unbelievably weird and completely counter-intuitive.