The tone, which often shows an unwillingness to learn. The lack of humility. Confidently stating or broadcasting facts without acknowledging why others might disagree.
This reflects more on you than the language: no language is perfect but if something is popular and successfully used for many projects, it’s unlikely that it reaches the level of “sucks”. It’s far more interesting to talk about what traits you like or don’t like because those are things which can be evaluated by readers who can mull how relevant your experience is to them. For example, if you say you’re working on embedded systems a lot of people will likely agree that Python isn’t a great fit but also that experience is only partially relevant to someone who builds web apps or works in data science.
It would be an okay provocative title if the criticism was well-founded (which can certainly be done for Python), but most of the article is rather poor.
Ayup. If you have these same points of view that the author does: please don't use Python. There is no shortage of languages out there that would be a better match for you--please use one of those.
However, the packaging and installation thing is very valid--Python really sucks at that and needs to fix it.
No, lists are implemented in CPython as an regular, contiguous array of PyObject. There is an array library (https://docs.python.org/3/library/array.html) for more efficient storage, comparable to JS's Uint8Array and friends.
I haven't had much occasion to use them. I suspect that most use cases nowadays where the speed advantage is needed involve numerical computing where the user is probably using a library like numpy or scipy and will use their array implementation, not the built-in Python one. However, arrays have been around in Python since very early on, when those other numerical libraries didn't exist, so the array built-in might have gotten more use then.
"Reason 7: Pass By Object Reference": isn't this the case of almost every GC'd language? Java, JS, PHP, Swift, and C# all do so, although C# doesn't if your type is a "struct" not a class. Go is the only popular exception I can think of.
Yep, one of the theoretical promises of FP that’s totally vindicated (unless there’s a language bug, but they usually get this much right). And because you can’t tell, you don’t need to care.
For folks working in imperative languages, you can achieve this same bliss by either not modifying your function parameters in the first place (hopefully this is obvious if you care about the distinction and find the language behavior ambiguous)… or by just doing whatever copy/clone/value transfer would be appropriate as a first step if you have good reason to modify the value of a parameter to your otherwise pure function.
Python is actually different in that the whole "pass by value" vs. "pass by reference" distinction doesn't really apply. At the C level, yes, every Python object is "passed by reference" in the sense that pointers to its C structure are what get passed around. But at the Python level, Python "variables" aren't labels for memory locations, as they are in other languages; they're namespace bindings. And a function can't access the caller's namespace at all unless the global or nonlocal keywords are explicitly used. So in many ways Python's function call semantics are more like "pass by value" than "pass by reference"; but they're really not quite the same as either one.
That history of Perl part seems a bit off. Perl 5 was wildly popular, and most of the early web was a mix of Perl 4 and Perl 5. People left for various reasons, many of them stemming from Perl 6 always being delayed and lack of momentum in Perl 5, but definitely not because Perl 6 was here and people didn't want to switch.
That's not to defend Python versioning though, which as a sysadmin supporting devs is always a pain point.
"My code for Python 3.5 won't work with the Python 3.7 installation unless I intentionally port it to 3.7."
Sure it will, just invoke it using the Python 3.7 interpreter. I have plenty of code originally written with 3.5 that runs just fine under 3.7, 3.8, 3.9, ...
"In Python, you have to work to pass variables by value."
No, you don't. Python actually doesn't pass variables "by reference". It passes object pointers, but you can't "mutate" an object pointer; it will always point to the same object forever.
What this author doesn't seem to get is that in Python, "variables" aren't labels for memory locations; they're namespace bindings. And unless you explicitly use the global or nonlocal keyword, nothing you do with setting variables inside a function will affect the caller's namespace. Which means that, in effect, Python variables are passed "by value" to functions--the function can only work with the object it was passed, it can't mess with the namespace it was passed from.
What does often trip up new Python programmers is that if you pass a mutable object, like a list or a dict, to a function, the function can mutate the object (add items, remove items, change what object a key or an index refers to). But the claim the author is making is much broader than that.
"Calling the same object by different names doesn't change the object, so it is effectively global."
I think this person simply doesn't understand how Python works.
> I think this person simply doesn't understand how Python works.
To be fair, if Python does something that is so different from all other languages maybe that’s a complaint in itself, even if the original complaint is invalid.
Do you think it does? CS is a field with many wild ideas and I don’t think python is that far into the weeds. Definitely not around 2018 when this was written. A lot of it’s big problems are 90s-scripting-language problems.
It's very easy to use day to day, much easier to grok than C pointers for sure, despite being a bit more limited (in a good way). I remember half the CS 101 class dropping the week pointers were introduced. If it's wrong, I don't wanna be right, as they say.
Even the title, to me, indicates that the article is going to be lousy. Rather than saying “reasons why I don’t like Python”, they present the title as if Python is objectively bad, which obviously isn’t true.
Recent counter example: if you have python code that uses the "U" open mode (e.g. open(file, "rU")), it doesn't work anymore with 3.11. Yes, it produced a deprecation warning for a few versions, but no, your script that works in python 3.x is not guaranteed to work in 3.y. There are plenty of other examples.
> it produced a deprecation warning for a few versions
Eight, to be exact: 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10. Python 3.3 was released more than 10 years ago. That's a long time for a feature to be deprecated before being removed altogether.
> your script that works in python 3.x is not guaranteed to work in 3.y
True, not "guaranteed" in every single respect. But spotting and fixing a deprecation warning is pretty simple. So are fixes for other examples of feature removals (and I don't think there are that many in Python 3). That's nothing like the picture the author was painting; he was talking as if code needs to be "ported" between each version, which is just nonsense.
3.0 or 4.0 is the time to drop a user-facing feature. If 3.11 is not backward compatible, they aren’t following semver. I won’t assume every minor version bump must break stuff, but I have to be aware that it might and budget time for fixes.
From https://peps.python.org/pep-0006/ it seems that 3.10.9 is expected to be compatible with 3.10.8, but users need to treat 3.11.0 like a potentially major rather than minor release. I wish they had labeled it 311.0 to communicate this and set expectations.
PEP 006 describes the way Python versioning has worked since Python was first released. Yes, it's technically not quite the same as current semver, but current semver didn't exist anyway back then.
That's a decade to make a 30 second change. Add something like https://github.com/asottile/pyupgrade to your pre-commit hooks and you won't even need 25 of those seconds.
Much of this seem to be the gripes of an inexperienced python user (e.g. experienced users tend to work with environment managers like miniconda, which turn the version hell the author complains about into a downright strength, making python environments considerably easier to manage than C environments).
And the author mangles their Perl history as well. Perl 5 was (and is) by far the most popular version of the language. It was Perl 6 which started a decline, and that might have been due to an overambitious scope, a huge delay in releasing a production ready version, and an Osborne effect as much as much as due to incompatibilities.
> * Type annotations are ignored, so nobody bothers.
For any serious Python app worked on by multiple people, you need a CI pipeline that runs type checking. (And while you're there, run a format checker like black, too -- solves the tabs vs spaces "debate" the author complains about as well)
Certainly still a language flaw that it's not inherently enforced, though; unfortunately it just took the Python community way too long to realize how important strong typing is at scale.
I was actually interested to read a list of quirks, especially since I haven't used Python in quite a while now and the author claims it has more quirks than any other language they've ever seen. And yet, the only example is the relatively easy to understand quoting. What is this article actually for?
Yeah, as much as I hate python (after 10 years of a career based solely in it), “There are only two kinds of languages: the ones people complain about and the ones nobody uses.”
Although if I made a list it probably wouldn't match this one. Reason 1 and 2 are basically the same, and yeah this sucks and I wish I could just build a static binary like Go. I'm surprised more languages don't make this very easy!
Reason 3, I can't care about the whitespace debate anymore. But
> Deep nesting is permitted, but lines can get so wide that they wrap lines in the text editor.
??? This article is 5 years old, and we have black now to solve this; but I'm pretty sure most production code bases have had hard line limits in the style guide.
Reason 4,
> Finding a list of what can be imported is non-intuitive. With C, you can just look in /usr/include/*.h. But with Python? It's best to use 'python -v' to list all of the places it looks, and then search every file in every directory and subdirectory from that list
I don't think C/C++ is actually that much better at this?
> In contrast, many Python modules include initialization functions that run during the import.
They can but they commonly don't. A lot of python is about "we're all adults here", and that works surprisingly well.
Reason 5
> In every other language, arrays are called 'arrays'. In Python, they are called 'lists'.
I mean there's a good reason for that, and I don't think C++ would call that data structure an `array` either.
> Python seems to go out of it's way to not use the common terms found
Honestly CS loves to give +3 names to basic things all over, this isn't new to python. I don't think the author's chosen ones are correct.
Reason 6, I think newer languages are doing similar things with strings that python did, so I'm gonna say the wider community things this was a decent thing to do. And I'm not sure triple quotes is as complicated as =/==/=== in JS.
Reason 7,
> This means that changing the source variable may end up changing the value.
If you mutate an object I'm not surprised the change is propagated. But there are immutable values, notably strings, that will not work this way. I can't say this author shows much experience with any language in depth.
Reason 8, I don't think I've ever heard of someone complain about namespacing like this, and then compliment C.
These reasons all feel the like "it's not like the first language I used" rather than real issues.
What about default mutable arguments? What about namespacing of loop variables (and other wild ass shadowing rules when you do nested functions)? What about tooling??? You haven’t lived until you’ve made pylint or MyPy crash. And when they work, they are slow! And MyPy can be unstable in results on large code bases!
> > Finding a list of what can be imported is non-intuitive. With C, you can just look in /usr/include/*.h. But with Python? It's best to use 'python -v' to list all of the places it looks, and then search every file in every directory and subdirectory from that list
> I don't think C/C++ is actually that much better at this?
Yeah, I really don’t know how someone could work with C and not have experience with include or linker path issues. The section on installation shows he doesn’t understand how Linux packaging works, though, so I think it’s something along the lines of a mental model which doesn’t include the extensive time which the Debian / Fedora maintainers have spent packaging that code, or that the reason why they’re not having versioning issues is that they’re using systems which only change the version on system upgrades. He could have avoided those issues with Python by only using the distribution packages just as with the C libraries, or done the inverse by preparing packages for a newer version of some C library.
As someone who writes Python 9 to 5, I much rather be writing Rust (which I do in my spare time) and would concur that Python generally sucks to write large code bases [0].
That being said, I really don't think any of the reasons here get to why python isn't fun to write large code bases in. I'd say my biggest pain point has more to do with it's typing system (which is getting better).
[0] Can it be done properly? Sure. But I personally feel worse about python the more complex a library I'm writing gets.
> an associative array is sometimes called a 'hash' (Perl), but Python calls it a 'dictionary'. Python seems to go out of it's way to not use the common terms found throughout the computer and information science field.
The author isn’t well-informed. “Dictionary” is the original and traditional name for associative arrays in computer science. Smalltalk uses it as well, as do many CS books on algorithms and data structures. See for example: https://www.nist.gov/dads/HTML/dictionary.html
I haven’t had skin in the game for years, so my opinion isn’t super valuable here.
Anyway, some of this is valid, albeit a lot of that is either well trodden (eg py3 gripes, pros/cons of significant whitespace) or host environment/tooling specific (eg where a thing installs `python` and which version without additional qualification and whether that’s stable).
But some of this is surprisingly mind boggling! I interrupted my reading of it somewhere in the beginning of
> Reason 7: Pass By Object Reference
The author spends quite a lot of time expressing familiarity with not just their preferred language C but demonstrating fairly intimate familiarity with several dynamic languages… several which have a reputation for exhibiting or even exacerbating exactly the same problem described. They even allude to some of that earlier. Huh?
I’d already glossed over the possibility to rename imports gripe (I think most module systems support this? Certainly the ones which augment the local namespace without any mechanism for disambiguation do, otherwise good luck ever getting anything done!)
If author is here reading, I’m not posting this to pile on, but to express honest confusion about how you’ve selected these gripes and how they line up for you with the seemingly greener pastures referenced. I don’t even much like Python either! I’m just getting a lot of cognitive dissonance trying to understand the perspective.
I agree with the sentiment that these reasons are terrible.
My main gripes:
- the half hearted take on static types
- excessive use of mutability and imperative programming
- dependency, version management and compatibility.
For this last one. Are there recommended best practices? I noticed that pip recently added a “constraints” file option which seems like it’s going towards at least the lock file level of guarantees that node has. Or do people still recommend using a separate manager (conda/miniconda).
I just want to say, working with python in an air gapped environment is fucking painful too.
I am not working with it directly but indirectly with ansible, and one of the playbooks (or "collections" in the latest iterations of ansible) has a stupid requirement to perform a pip install some random packages. Maybe there is a way to compile those packages offline and tell pip to use a tar ball. But I couldn't figure it out so I just commented out those specific steps. I even went so far as to grok the repo for any references to this package but couldn't find anything.
On the other hand, I do appreciate python's concise syntax and makes it easy to write a few powerful one liners.
73 comments
[ 5.3 ms ] story [ 138 ms ] thread(Slashdot wants their post back, haha.)
Uh, no. There’s a reason I prefer Node, Deno or Rust, and the reason is that Python sucks.
Or at least sucks more than either of those.
The main benefit I hear people say Python brings are the libraries that are written in it, but that’s not really a property of the language itself.
However, the packaging and installation thing is very valid--Python really sucks at that and needs to fix it.
Lists in python are called lists because they're linked lists right? They are not called arrays because they are a different data structure.
Edit: Well TIL! Thanks everyone.
No. At the C level in the interpreter they are arrays of object pointers.
> They are not called arrays because they are a different data structure.
No. They're not called arrays because Python uses the name "array" for something else:
https://docs.python.org/3/library/array.html
For folks working in imperative languages, you can achieve this same bliss by either not modifying your function parameters in the first place (hopefully this is obvious if you care about the distinction and find the language behavior ambiguous)… or by just doing whatever copy/clone/value transfer would be appropriate as a first step if you have good reason to modify the value of a parameter to your otherwise pure function.
That's not to defend Python versioning though, which as a sysadmin supporting devs is always a pain point.
"My code for Python 3.5 won't work with the Python 3.7 installation unless I intentionally port it to 3.7."
Sure it will, just invoke it using the Python 3.7 interpreter. I have plenty of code originally written with 3.5 that runs just fine under 3.7, 3.8, 3.9, ...
"In Python, you have to work to pass variables by value."
No, you don't. Python actually doesn't pass variables "by reference". It passes object pointers, but you can't "mutate" an object pointer; it will always point to the same object forever.
What this author doesn't seem to get is that in Python, "variables" aren't labels for memory locations; they're namespace bindings. And unless you explicitly use the global or nonlocal keyword, nothing you do with setting variables inside a function will affect the caller's namespace. Which means that, in effect, Python variables are passed "by value" to functions--the function can only work with the object it was passed, it can't mess with the namespace it was passed from.
What does often trip up new Python programmers is that if you pass a mutable object, like a list or a dict, to a function, the function can mutate the object (add items, remove items, change what object a key or an index refers to). But the claim the author is making is much broader than that.
"Calling the same object by different names doesn't change the object, so it is effectively global."
I think this person simply doesn't understand how Python works.
To be fair, if Python does something that is so different from all other languages maybe that’s a complaint in itself, even if the original complaint is invalid.
No, it's just a reason not to use Python if you have trouble understanding how it works. That's true of any tool.
Eight, to be exact: 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10. Python 3.3 was released more than 10 years ago. That's a long time for a feature to be deprecated before being removed altogether.
> your script that works in python 3.x is not guaranteed to work in 3.y
True, not "guaranteed" in every single respect. But spotting and fixing a deprecation warning is pretty simple. So are fixes for other examples of feature removals (and I don't think there are that many in Python 3). That's nothing like the picture the author was painting; he was talking as if code needs to be "ported" between each version, which is just nonsense.
And the author mangles their Perl history as well. Perl 5 was (and is) by far the most popular version of the language. It was Perl 6 which started a decline, and that might have been due to an overambitious scope, a huge delay in releasing a production ready version, and an Osborne effect as much as much as due to incompatibilities.
Why can’t we just call it python-version-manager?
But then what would we call the other 15 methods of managing python dependencies/versions?
* Performance is very bad.
* Misspelling a variable creates a new one.
* __init__ is a really terrible name for the constructor.
* Type annotations are ignored, so nobody bothers.
IDEs/linters help with this one.
> Type annotations are ignored, so nobody bothers
If one really cares you can use MyPy to force errors on bad types.
Type annotations are very useful for IDEs, which is why they are spreading fast in the ecosystem.
For any serious Python app worked on by multiple people, you need a CI pipeline that runs type checking. (And while you're there, run a format checker like black, too -- solves the tabs vs spaces "debate" the author complains about as well)
Certainly still a language flaw that it's not inherently enforced, though; unfortunately it just took the Python community way too long to realize how important strong typing is at scale.
Although if I made a list it probably wouldn't match this one. Reason 1 and 2 are basically the same, and yeah this sucks and I wish I could just build a static binary like Go. I'm surprised more languages don't make this very easy!
Reason 3, I can't care about the whitespace debate anymore. But
> Deep nesting is permitted, but lines can get so wide that they wrap lines in the text editor.
??? This article is 5 years old, and we have black now to solve this; but I'm pretty sure most production code bases have had hard line limits in the style guide.
Reason 4,
> Finding a list of what can be imported is non-intuitive. With C, you can just look in /usr/include/*.h. But with Python? It's best to use 'python -v' to list all of the places it looks, and then search every file in every directory and subdirectory from that list
I don't think C/C++ is actually that much better at this?
> In contrast, many Python modules include initialization functions that run during the import.
They can but they commonly don't. A lot of python is about "we're all adults here", and that works surprisingly well.
Reason 5
> In every other language, arrays are called 'arrays'. In Python, they are called 'lists'.
I mean there's a good reason for that, and I don't think C++ would call that data structure an `array` either.
> Python seems to go out of it's way to not use the common terms found
Honestly CS loves to give +3 names to basic things all over, this isn't new to python. I don't think the author's chosen ones are correct.
Reason 6, I think newer languages are doing similar things with strings that python did, so I'm gonna say the wider community things this was a decent thing to do. And I'm not sure triple quotes is as complicated as =/==/=== in JS.
Reason 7,
> This means that changing the source variable may end up changing the value.
If you mutate an object I'm not surprised the change is propagated. But there are immutable values, notably strings, that will not work this way. I can't say this author shows much experience with any language in depth.
Reason 8, I don't think I've ever heard of someone complain about namespacing like this, and then compliment C.
These reasons all feel the like "it's not like the first language I used" rather than real issues.
What about default mutable arguments? What about namespacing of loop variables (and other wild ass shadowing rules when you do nested functions)? What about tooling??? You haven’t lived until you’ve made pylint or MyPy crash. And when they work, they are slow! And MyPy can be unstable in results on large code bases!
> I don't think C/C++ is actually that much better at this?
Yeah, I really don’t know how someone could work with C and not have experience with include or linker path issues. The section on installation shows he doesn’t understand how Linux packaging works, though, so I think it’s something along the lines of a mental model which doesn’t include the extensive time which the Debian / Fedora maintainers have spent packaging that code, or that the reason why they’re not having versioning issues is that they’re using systems which only change the version on system upgrades. He could have avoided those issues with Python by only using the distribution packages just as with the C libraries, or done the inverse by preparing packages for a newer version of some C library.
* The standard library still sucks
* The documentation still sucks
* Dependency management still sucks (unless you cope with poetry).
* it doesn't have CPAN
* It's not a lisp
Reasons Python Sucks - https://news.ycombinator.com/item?id=18706174 - Dec 2018 (535 comments)
That being said, I really don't think any of the reasons here get to why python isn't fun to write large code bases in. I'd say my biggest pain point has more to do with it's typing system (which is getting better).
[0] Can it be done properly? Sure. But I personally feel worse about python the more complex a library I'm writing gets.
The author isn’t well-informed. “Dictionary” is the original and traditional name for associative arrays in computer science. Smalltalk uses it as well, as do many CS books on algorithms and data structures. See for example: https://www.nist.gov/dads/HTML/dictionary.html
Anyway, some of this is valid, albeit a lot of that is either well trodden (eg py3 gripes, pros/cons of significant whitespace) or host environment/tooling specific (eg where a thing installs `python` and which version without additional qualification and whether that’s stable).
But some of this is surprisingly mind boggling! I interrupted my reading of it somewhere in the beginning of
> Reason 7: Pass By Object Reference
The author spends quite a lot of time expressing familiarity with not just their preferred language C but demonstrating fairly intimate familiarity with several dynamic languages… several which have a reputation for exhibiting or even exacerbating exactly the same problem described. They even allude to some of that earlier. Huh?
I’d already glossed over the possibility to rename imports gripe (I think most module systems support this? Certainly the ones which augment the local namespace without any mechanism for disambiguation do, otherwise good luck ever getting anything done!)
If author is here reading, I’m not posting this to pile on, but to express honest confusion about how you’ve selected these gripes and how they line up for you with the seemingly greener pastures referenced. I don’t even much like Python either! I’m just getting a lot of cognitive dissonance trying to understand the perspective.
My main gripes:
- the half hearted take on static types
- excessive use of mutability and imperative programming
- dependency, version management and compatibility.
For this last one. Are there recommended best practices? I noticed that pip recently added a “constraints” file option which seems like it’s going towards at least the lock file level of guarantees that node has. Or do people still recommend using a separate manager (conda/miniconda).
I am not working with it directly but indirectly with ansible, and one of the playbooks (or "collections" in the latest iterations of ansible) has a stupid requirement to perform a pip install some random packages. Maybe there is a way to compile those packages offline and tell pip to use a tar ball. But I couldn't figure it out so I just commented out those specific steps. I even went so far as to grok the repo for any references to this package but couldn't find anything.
On the other hand, I do appreciate python's concise syntax and makes it easy to write a few powerful one liners.