8 Reasons Python Sucks
Saturday, 15 December 2018
Occasionally I go out to lunch with some of my techie friends and we have a great time geeking together. We talk about projects, current events, and various tech-related issues. Inevitably, the discussion will turn to programming languages. One might lament "I have to modify some Java code. I hate Java. (Oh, sorry, Kyle.)" (It probably doesn't help that we gave Kyle the nickname "Java-boy" over a decade ago.) Another will gripe about some old monolithic shell code that nobody wants to rewrite.
And me, well... I just blurted it out: I hate Python. I hate it with a passion. If I have the choice between using some pre-existing Python code or rewriting it in C, I'd rather rewrite it in C.
When I finished shouting, Bill humorously added, "But what do you really think about Python, Neal?" So I'm dedicating this blog entry to Bill.
Here's my list of "8 reasons Python sucks".
I'm all for adding new functionality to languages. I don't even mind if some old version becomes obsolete. However, Python installs in separate installations. My code for Python 3.5 won't work with the Python 3.7 installation unless I intentionally port it to 3.7. Enough Linux developers have decided that porting isn't worth the effort, so Ubuntu installs with both Python2 and Python3 -- because they are needed by different core functions.
This lack of backwards compatibility and split versions is usually a death knell. Commodore created one of the first home computers (long before the IBM PC or Apple). But the Commodore PET wasn't compatible with the subsequent Commodore CBM computer. And the CBM wasn't compatible with the VIC-20, Commodore-64, Amiga, etc. So either you spent a lot of time porting code from one platform to another, or you abandoned the platform. (Where's Commodore today? It died out as users abandoned the platform.)
Similarly, Perl used to be very popular. But when Perl3 came out, it wasn't fully backwards compatible with a lot of Perl2 code. The community griped, good code was ported, and the rest was abandoned. Then came Perl4 and the same thing happened. When Perl5 came out, a lot of people just switched to a different programming language that was more stable. Today, there's only a small community of people actively using Perl to maintain existing Perl projects. I haven't seen any major new projects based on Perl.
By the same means, Python has distinct silos of code for each version. And the community keeps dragging along the old versions. So you end up with a lot of old, dead Python code that keeps getting dragged along because nobody wants to spend the time porting it to the latest version. As far as I can tell, nobody creates new code for Python2, but we drag it along because nobody's ported the needed code to Python3.x. At the official Python web site, their documentation is actively maintained and available for Python 2.7, 3.5, 3.6, and 3.7 -- because they can't decide to give up on the old code. Python is like the zombie of programming languages -- the dead just keep walking on.
So instead, you install the version of Python you need. For one of the projects I was on, we used Python. But we had to use Python3.5 (the latest at that time). My computer ended up with Python2, Python2.6, Python3, and Python3.5 installed. Two were from the operating system, one was for the project, and one came in because of some unrelated software I installed for some other reason. Even though they are all "Python", they are not all the same.
If you want to install packages for Python, you're supposed to use "pip". (Pip stands for "Pip Installs Packages", because someone thinks recursive acronyms are still funny.) But since there's a bunch of versions of Python on the system, you have to remember to use the correct version of pip. Otherwise, 'pip' might run 'pip2' and not the 'pip3.7' that you need. (And you need to specify the actual path for pip3.7 if the name doesn't exist.)
I was advised by one teammate that I needed to configure my environment so that everything uses the Python 3.5 base. This worked great until I started on a second project that needed Python 3.6. Two concurrent projects with two different versions of Python -- no, that wasn't confusing. (What's the emoticon for sarcasm?)
The pip installer places files in the user's local directory. You don't use pip to install system-wide libraries. And Gawd forbid you make the mistake of running 'sudo pip', because that can screw up your entire computer! Running sudo might make some packages install at the system level, some install for the wrong version of Python, and some files in your home directory might end up being owned by root, so future non-sudo pip installs may fail due to permissions. Just don't do it.
By the way, who maintains these pip modules? The community. That is, no clear owner and no enforced chain of provenance or accountability. Earlier this year, a version of PyPI was found to have a backdoor that stole SSH credentials. This doesn't surprise me at all. (I don't use Node.js and npm for the same reason; I don't trust their community repositories.)
Most programming languages use some kind of notation to identify scope -- where a function begins and ends, actions contained in a conditional statement, range of a variable's definition, etc. With C, Java, JavaScript, Perl, and PHP, braces {...} define the scope. Lisp uses parenthesis (...). And Python? It uses spaces. If you need to define a scope for complex code, then you indent the next few lines. The scope ends when the indent ends.
The Python manual says that you can use any number of spaces or tabs for defining the scope. However, ALWAYS USE FOUR SPACES PER INDENT! If you want to indent twice for nesting, use eight spaces! The Python community has standardized on this nomenclature, even though it isn't in the Python manual. Forget the fact that the examples in the documentation use tabs, tabs + 1 space, and other indents. The community is rabid about using four spaces. So unless you plan to never show your code to anyone else, always use four spaces for each indent.
When I first saw Python code, I thought that using indents to define the scope seemed like a good idea. However, there's a huge downside. Deep nesting is permitted, but lines can get so wide that they wrap lines in the text editor. Long functions and long conditional actions may make it hard to match the start to the end. And I pity anyone who miscounts spaces and accidentally puts in three spaces instead of four somewhere -- this can take hours to debug and track down.
For other languages, I've picked up the habit of putting debug code without any indents. This way, I can quickly browse the code and easily identify and remove debugging code when I'm done. But with Python? Anything not indented properly generates an indention error. This means debugging code must blend in to the active code.
Python's import permits including an entire module, part of a module, or a specific function from a module. 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 have friends who love Python and I've seen them grep through standard modules as they look for the thing they want to import. Seriously.
The import function also allows users to rename the imported code. They basically define a custom namespace. At first glance, this might seem nice, but it ends up impacting readability and long-term support. Renaming modules is great for small scripts, but really bad for long programs. People who use 1-2 letter namespaces, like "import numpy as n" should be shot (or forced to convert all of their code to Perl5).
But that's not the worst part. With most languages, including code just includes the code. A few languages, like object-oriented C++, may execute code if there's a global object with a constructor. Similarly, some PHP code may define global variables, so an import could run code -- but that's typically considered a bad practice. In contrast, many Python modules include initialization functions that run during the import. You don't know what's running, you don't know what it does, and you might not notice. Unless there's a namespace conflict, in which case you get to spend many fun hours tracking down the cause.
And then there are the names of libraries. PyPy, PyPi, NumPy, SciPy, SymPy, PyGtk, Pyglet, PyGame... (Yes, those first two are pronounced the same way, but they do very different things.) I understand that the 'py' is for Python. But couldn't they be consistent about whether it comes first or second?
Some common libraries just gave up on the pun-like "Py" naming convention. This includes, matplotlib, nose, Pillow, and SQLAlchemy. And while some of the names may give you a hint to the purpose (e.g., "SQLAlchemy" contains SQL, so it's probably an SQL interface), others are just random words. If you didn't know what "BeautifulSoup" did, could you tell from the name that it's an HTML/XML parser? (As an aside, BeautifulSoup is well documented and easy to use. If every Python module was like this, I wouldn't be complaining so much. Unfortunately, this is the exception and not the norm. Most Python libraries seriously suck at documentation.)
Overall, I view Python as a collection of libraries with horrible and inconsistent naming conventions. I have a standing gripe that open source projects typically have horrible names. Unless you know the project, you'll never figure out what it does by the name. And unless you know what to look for, you'll probably only find it by accidentally stumbling across someone who mentions it in passing. Most of Python's libraries reinforce this negative criticism.
This is one of the big differences between procedural, functional, and object-oriented programming languages. If every variable is passed by object reference, and any change to the variable changes the reference everywhere, then you might as well use globals for everything. Calling the same object by different names doesn't change the object, so it is effectively global. And as C programmers learned long ago, global variables are evil and should not be used.
In Python, you have to work to pass variables by value. Saying "a=b" just assigns another name to the same object space; this doesn't copy the value of b into a. If you actually meant to copy the value, then you need to use a copy function. Usually this is "a=b.copy()". However, notice that I said "usually". Not all data types have a 'copy' prototype. Or maybe the copy function is incomplete. In those cases, there is a separate library called 'copy' that you can use: "a=copy.deepcopy(b)".
With C, Java, JavaScript, Perl, PHP, etc., this works fine because the language can easily distinguish resource libraries from the local program; they have different paths. But with Python? Don't do this. Never do this. Why? Python assumes you want to import the local code first. If I have a program called "screencapture.py" that uses "import screencapture", then it will import itself rather than the system library. At minimum, you should call your local program "myscreencapture.py" instead.
My friends often cite all of the really cool Python libraries that exist. And I agree that some of the libraries are really useful. For example, BeautifulSoup is one of the best HTML parsers I've ever used, NumPy makes multidimensional arrays and complex mathematics easier to implement, and TensorFlow is very useful for machine learning. However, I'm not going to make a monolithic program in Python just because I like TensorFlow or SciPy. I'm not going to give up readability and maintainability for a free pony; it's not worth the effort.
Usually when I write negative criticisms about a topic, I also try to write something positive. I followed my blog entry on "Open Source Sucks" with "Open Source Rocks". And when I wrote about limitations with FFmpeg, I explicitly mentioned how it's the best video processing library out there. But I can't make a list of good things about Python because I really think that Python sucks.
(Hey Bill, does this answer your question?)
And me, well... I just blurted it out: I hate Python. I hate it with a passion. If I have the choice between using some pre-existing Python code or rewriting it in C, I'd rather rewrite it in C.
When I finished shouting, Bill humorously added, "But what do you really think about Python, Neal?" So I'm dedicating this blog entry to Bill.
Here's my list of "8 reasons Python sucks".
Reason 1: Versions
If you install a default Linux operating system, there's a really good chance that it will install multiple versions of Python. It will probably have Python2 and Python3, and maybe even some fractional versions like 3.5 or 3.7. There's a reason for this: Python3 is not fully compatible with Python2. Even some of the fractional versions are distinct enough to lack backwards compatibility.I'm all for adding new functionality to languages. I don't even mind if some old version becomes obsolete. However, Python installs in separate installations. My code for Python 3.5 won't work with the Python 3.7 installation unless I intentionally port it to 3.7. Enough Linux developers have decided that porting isn't worth the effort, so Ubuntu installs with both Python2 and Python3 -- because they are needed by different core functions.
This lack of backwards compatibility and split versions is usually a death knell. Commodore created one of the first home computers (long before the IBM PC or Apple). But the Commodore PET wasn't compatible with the subsequent Commodore CBM computer. And the CBM wasn't compatible with the VIC-20, Commodore-64, Amiga, etc. So either you spent a lot of time porting code from one platform to another, or you abandoned the platform. (Where's Commodore today? It died out as users abandoned the platform.)
Similarly, Perl used to be very popular. But when Perl3 came out, it wasn't fully backwards compatible with a lot of Perl2 code. The community griped, good code was ported, and the rest was abandoned. Then came Perl4 and the same thing happened. When Perl5 came out, a lot of people just switched to a different programming language that was more stable. Today, there's only a small community of people actively using Perl to maintain existing Perl projects. I haven't seen any major new projects based on Perl.
By the same means, Python has distinct silos of code for each version. And the community keeps dragging along the old versions. So you end up with a lot of old, dead Python code that keeps getting dragged along because nobody wants to spend the time porting it to the latest version. As far as I can tell, nobody creates new code for Python2, but we drag it along because nobody's ported the needed code to Python3.x. At the official Python web site, their documentation is actively maintained and available for Python 2.7, 3.5, 3.6, and 3.7 -- because they can't decide to give up on the old code. Python is like the zombie of programming languages -- the dead just keep walking on.
Reason 2: Installation
With most software packages, you can easily run apt, yum, rpm, or some other install base and get the most recent code. That isn't the case with Python. If you install using 'apt-get install python', you don't know what version you're actually installing, and it may not be compatible with all of the code you need.So instead, you install the version of Python you need. For one of the projects I was on, we used Python. But we had to use Python3.5 (the latest at that time). My computer ended up with Python2, Python2.6, Python3, and Python3.5 installed. Two were from the operating system, one was for the project, and one came in because of some unrelated software I installed for some other reason. Even though they are all "Python", they are not all the same.
If you want to install packages for Python, you're supposed to use "pip". (Pip stands for "Pip Installs Packages", because someone thinks recursive acronyms are still funny.) But since there's a bunch of versions of Python on the system, you have to remember to use the correct version of pip. Otherwise, 'pip' might run 'pip2' and not the 'pip3.7' that you need. (And you need to specify the actual path for pip3.7 if the name doesn't exist.)
I was advised by one teammate that I needed to configure my environment so that everything uses the Python 3.5 base. This worked great until I started on a second project that needed Python 3.6. Two concurrent projects with two different versions of Python -- no, that wasn't confusing. (What's the emoticon for sarcasm?)
The pip installer places files in the user's local directory. You don't use pip to install system-wide libraries. And Gawd forbid you make the mistake of running 'sudo pip', because that can screw up your entire computer! Running sudo might make some packages install at the system level, some install for the wrong version of Python, and some files in your home directory might end up being owned by root, so future non-sudo pip installs may fail due to permissions. Just don't do it.
By the way, who maintains these pip modules? The community. That is, no clear owner and no enforced chain of provenance or accountability. Earlier this year, a version of PyPI was found to have a backdoor that stole SSH credentials. This doesn't surprise me at all. (I don't use Node.js and npm for the same reason; I don't trust their community repositories.)
Reason 3: Syntax
I'm a strong believer in readable code. And at first glance, Python seems very readable. That is, until you start making large code bases.Most programming languages use some kind of notation to identify scope -- where a function begins and ends, actions contained in a conditional statement, range of a variable's definition, etc. With C, Java, JavaScript, Perl, and PHP, braces {...} define the scope. Lisp uses parenthesis (...). And Python? It uses spaces. If you need to define a scope for complex code, then you indent the next few lines. The scope ends when the indent ends.
The Python manual says that you can use any number of spaces or tabs for defining the scope. However, ALWAYS USE FOUR SPACES PER INDENT! If you want to indent twice for nesting, use eight spaces! The Python community has standardized on this nomenclature, even though it isn't in the Python manual. Forget the fact that the examples in the documentation use tabs, tabs + 1 space, and other indents. The community is rabid about using four spaces. So unless you plan to never show your code to anyone else, always use four spaces for each indent.
When I first saw Python code, I thought that using indents to define the scope seemed like a good idea. However, there's a huge downside. Deep nesting is permitted, but lines can get so wide that they wrap lines in the text editor. Long functions and long conditional actions may make it hard to match the start to the end. And I pity anyone who miscounts spaces and accidentally puts in three spaces instead of four somewhere -- this can take hours to debug and track down.
For other languages, I've picked up the habit of putting debug code without any indents. This way, I can quickly browse the code and easily identify and remove debugging code when I'm done. But with Python? Anything not indented properly generates an indention error. This means debugging code must blend in to the active code.
Reason 4: Includes
Most programming languages have some way to include other chunks of code. For C, it's "#include". For PHP, there's 'include', 'include_once', 'require', and 'require_once'. And for Python, there's "import".Python's import permits including an entire module, part of a module, or a specific function from a module. 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 have friends who love Python and I've seen them grep through standard modules as they look for the thing they want to import. Seriously.
The import function also allows users to rename the imported code. They basically define a custom namespace. At first glance, this might seem nice, but it ends up impacting readability and long-term support. Renaming modules is great for small scripts, but really bad for long programs. People who use 1-2 letter namespaces, like "import numpy as n" should be shot (or forced to convert all of their code to Perl5).
But that's not the worst part. With most languages, including code just includes the code. A few languages, like object-oriented C++, may execute code if there's a global object with a constructor. Similarly, some PHP code may define global variables, so an import could run code -- but that's typically considered a bad practice. In contrast, many Python modules include initialization functions that run during the import. You don't know what's running, you don't know what it does, and you might not notice. Unless there's a namespace conflict, in which case you get to spend many fun hours tracking down the cause.
Reason 5: Nomenclature
In every other language, arrays are called 'arrays'. In Python, they are called 'lists'. And 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.And then there are the names of libraries. PyPy, PyPi, NumPy, SciPy, SymPy, PyGtk, Pyglet, PyGame... (Yes, those first two are pronounced the same way, but they do very different things.) I understand that the 'py' is for Python. But couldn't they be consistent about whether it comes first or second?
Some common libraries just gave up on the pun-like "Py" naming convention. This includes, matplotlib, nose, Pillow, and SQLAlchemy. And while some of the names may give you a hint to the purpose (e.g., "SQLAlchemy" contains SQL, so it's probably an SQL interface), others are just random words. If you didn't know what "BeautifulSoup" did, could you tell from the name that it's an HTML/XML parser? (As an aside, BeautifulSoup is well documented and easy to use. If every Python module was like this, I wouldn't be complaining so much. Unfortunately, this is the exception and not the norm. Most Python libraries seriously suck at documentation.)
Overall, I view Python as a collection of libraries with horrible and inconsistent naming conventions. I have a standing gripe that open source projects typically have horrible names. Unless you know the project, you'll never figure out what it does by the name. And unless you know what to look for, you'll probably only find it by accidentally stumbling across someone who mentions it in passing. Most of Python's libraries reinforce this negative criticism.
Reason 6: Quirks
Every language has its quirks. With C, there's the weird nomenclature of using & and * for accessing address space and values. C also has that increment/decrement shortcut using ++ and --. With Bash, there's the whole "when to use a backslash" when quoting special characters like parenthesis and periods for regular expressions. And JavaScript has issues around compatibility (not every browser supports every useful function). However, Python has more quirks than any other language I've ever seen. Consider strings:- In C, double quotes enclose strings. Single quotes enclose characters.
- In PHP and Bash, both types of quotes can enclose strings. However, a double quote can have variables embedded in the string. In contrast, single quoted strings are literals; any embedded variable-like names are not expanded.
- In JavaScript, there's really no difference between single quotes and double quotes.
- In Python, there's no difference between single quotes and double quotes. However, if you want your string to span lines, then you need to use triple quotes """string""" or '''string'''. And if you want to use binary, then you need to preference the string with b (b'binary') or r (r'raw'). And sometimes you need to cast your strings as strings using str(string), or convert it to utf8 using string.encode('utf-8').
Reason 7: Pass By Object Reference
Most programming languages pass function parameters by value. If the function alters the value, the results are not passed back to the calling code. But as I've already explained, Python goes out of its way to be different. Python defaults to doing functions with pass-by-object-reference parameters. This means that changing the source variable may end up changing the value.This is one of the big differences between procedural, functional, and object-oriented programming languages. If every variable is passed by object reference, and any change to the variable changes the reference everywhere, then you might as well use globals for everything. Calling the same object by different names doesn't change the object, so it is effectively global. And as C programmers learned long ago, global variables are evil and should not be used.
In Python, you have to work to pass variables by value. Saying "a=b" just assigns another name to the same object space; this doesn't copy the value of b into a. If you actually meant to copy the value, then you need to use a copy function. Usually this is "a=b.copy()". However, notice that I said "usually". Not all data types have a 'copy' prototype. Or maybe the copy function is incomplete. In those cases, there is a separate library called 'copy' that you can use: "a=copy.deepcopy(b)".
Reason 8: Local Names
It's a common programming technique to name the program after the library or function being used. For example, if I'm testing a screen capture program with a C library called "libscreencapture.so", I would call my program "screencapture.c" and compile into "screencapture.exe".gcc -o screencapture.exe screencapture.c -lscreencapture
With C, Java, JavaScript, Perl, PHP, etc., this works fine because the language can easily distinguish resource libraries from the local program; they have different paths. But with Python? Don't do this. Never do this. Why? Python assumes you want to import the local code first. If I have a program called "screencapture.py" that uses "import screencapture", then it will import itself rather than the system library. At minimum, you should call your local program "myscreencapture.py" instead.
Not All Bad
Python is a very popular language and has a huge following. I even have a handful of friends who really like Python -- it's their preferred programming language. Over the years, I've discussed these issues with them, and each time they nod their heads and agree. They don't disagree that these are problems with Python; they just think it's not bad enough for them to stop loving the language.My friends often cite all of the really cool Python libraries that exist. And I agree that some of the libraries are really useful. For example, BeautifulSoup is one of the best HTML parsers I've ever used, NumPy makes multidimensional arrays and complex mathematics easier to implement, and TensorFlow is very useful for machine learning. However, I'm not going to make a monolithic program in Python just because I like TensorFlow or SciPy. I'm not going to give up readability and maintainability for a free pony; it's not worth the effort.
Usually when I write negative criticisms about a topic, I also try to write something positive. I followed my blog entry on "Open Source Sucks" with "Open Source Rocks". And when I wrote about limitations with FFmpeg, I explicitly mentioned how it's the best video processing library out there. But I can't make a list of good things about Python because I really think that Python sucks.
(Hey Bill, does this answer your question?)
Some nitpicking: You probably did not end up with 4 versions of Python. python2 and python3 are usually symlinks to the (latest) minor Python version. Making Python 3 backwards incompatible in so many (subtle) ways has, I think, generally been considered a problem and is unlikely to be repeated. To my knowledge, Python versions have been backwards compatibility in general lately, with the exception of modules like asyncio which have been marked as experimental.
Regarding nomenclature: While I do agree that "dict(ionary)" is a bit off, "list" makes perfect sense to me. An array usually has the property in terms of runtime complexity that you can't insert at an arbitrary position without moving elements. This is not true for lists in Python.
I also think that strings and their implementation makes sense. Granted, I don't know if distinguishing multi and single line strings is really necessary (or useful) but I really like the possibility to prefix a string literal. There are good reasons that strings are not just bytes (and the other way around) and disabling of escape sequences (raw strings) makes also sense to me.
Thanks for the feedback.
I ran off and checked my dev server. (The project is long over, but the VM is still lying around.)
python == 3.5.2 (from OS)
python2 == 2.7.6 (from OS)
python3 == 3.5.2 (from OS)
python3.5 == 3.5.6 (manually installed for project #1, resides in a separate directory/environment)
python3.7 == 3.7.0 (manually installed for project #2, separate directory/environment)
(I had python3.6 installed, but we eventually updated to 3.7.)
As you can tell, I'm not a huge python fan. But when one of my teammates says "you must run this specific version of python", that's what I do.
Pipenv, Virtualenv or even condas environments will change your life... Well if your life revolves around Python
The PyCharm IDE (JetBrains team) on both Windows and Linux does a very nice job of keep versions separate, and projects in their own virtualenv as well. You don't need an IDE for that, but I find it helps. I might die without PyCharm's ease of refactoring.
Here I am hating on the language I love, but I remember when I would have to review this question on StackOverflow every time I wanted to create a new virtual environment:
https://stackoverflow.com/questions/41573587/what-is-the-difference-between-venv-pyvenv-pyenv-virtualenv-virtualenvwrappe What a mess.
I was grateful that PyCharm finally ended that pain, as it handles it for both version 2 and 3.
Strings are thankfully much better in version 3, finally default utf-8!
Yay that all the packages I've ever needed have been ported to 3 by now. It was bad for a while. I can imagine that some specialized packages remain left behind.
"Use an IDE" isn't the best defense for a language's flaws, but then again JetBrains keeps delivering what I didn't even realize I needed. I don't code in C# nearly as much today but ReSharper was (is?) a complete game changer. That was a purchase for me back then; PyCharm Community Edition is free however, and works a treat.
Having read your blog quite a long time now, I doubt you're about to run open-armed to it... but sometimes I'm wrong. Cheers, and good post.
I manage my non-OS versions with `pyenv` but sadly, there doesn't seem to be much in the way of alternatives to the virtualenv system for vendoring packages. Everyone seems to agree that virtualenv is terrible, but they all insist on building on top of it.
You're wrong about Perl and versions. As a long-time coder and (non-obsessive) fan of the language. IIRC Perl's backward compatibility has always been pretty good. Perl 5 quickly came to dominance, especially with mod_perl. And Perl 5's backward compatibility in particular, despite many new features, is legendary.
import numpy as np
import pandas as pd
This is pretty standard across the board. I would hazard to guess if the reader of code hasn't read this sort of code, they are very junior to this area of python. Not sure why the shooting is needed.
If your function is too large it means that it is a strong candidate to be refactored in smaller functions.
In any programming language large functions are a bad practice.
That is, the indentation of python encourages the programmer to write clean code with smaller and atomic functions.
Are you serious?
Type "collection list" and "collection dictionary" into Google, for starters.
Here's but one example.
>>> [f(1) for f in [ lambda(x) : x+1 for x in range(10)]]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
Why is this scoping strange @lab27? Obviously function arguments defined within the function scope should override variables defined outside the function, this is true in all block and function-scoped languages.
>>> [f(1) for f in [lambda y: x+y for x in range(10)]]
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
It's really just a lexical closure... you shouldn't be expecting this expression to create a "new y" for the lambda expression every time through the loop. The behavior may be a bit surprising in this context, yes, but if you're going to use a lambda expression in a nested list comprehension you'd better understand enough about your language to think through what it actually means.
Python's only crime here is to give you enough rope to hang yourself, which is something normally Perl is more famous for. In truth Python is more powerful than people often think, but using this power in "clever" ways is usually discouraged, in part by syntax and in part by convention.
In the case of lambda for example, they are rather crippled by syntax (being limited to a single expression) to discourage this kind of thing; don't write LISP code using Python, use lambdas only for things like returning a sorting key, etc. But they are in the language, so if you REALLY insist you can abuse them.
BeautifulSoup derives its name from "Tag Soup" (https://www.wikiwand.com/en/Tag_soup).
I've worked on many Python codebases in the 100KLOC+ range, and readability does not decrease with size. If you're nesting your code so deep that it's going off the edge of your screen, that's a code smell in any language.
...and I agree with the author....having to maintain a non-trivial Python code base that you didn't write is a nightmare...
Python...like most languages initially created to be scripting languages to be used to take care of small automation tasks...it, do to it being easily picked up; started to be used for purposes it's design is not optimized for.
Just my opinion....
Import errors that are hidden are indeed a problem, I hope to see a solution. But code inclusion, where other code is context-dependent is even harder to debug and less predictable.
And you made good point about pass-by-object: it does create side effects.
The other thing I dearly miss in Python is JIT compilation. The existing JIT projects all have their own set of compromises. Why is there no standard @jit decorator? Or why is it not just done automatically, like .Net? Once we have first-class strict static type support, compilation can produce very good code where it matters.
Python's list are not called arrays, because python has actual arrays in the traditional sense: https://docs.python.org/3/library/array.html (It's in the standard library, but it has been there from the very beginning). Classically, 'arrays' are fixed-size collections of data of the same type. Python arrays are not at all like this, neither their behaviour, nor their interface let alone their implementation. The term's meaning has been extended by many other modern languages. But i think python shouldn't be blamed for sticking to classical terminology. If you really want to blame someone, it's all the other languages doing to wrong
Python is a string handling language, like Lisp and Logo.
An "array" in Python is only a string list, to get the parts out you have to use string slicing, like Atari BASIC.
There are alternatives also, you could use Haxe, which provides for a more JSish stynax and target Python 3.
https://pipenv.readthedocs.io/
it might help with some of your versioning/environment issues.
A dict(ionary) is relatively common, as is a map, both of which make more sense that a hash, which is just something that most dictionaries use as an implementation detail.
Passing by object reference is fairly standard in high-level OO languages. In fact, all the popular ones do it except C, AFAIK.
When it comes to library names, there are terrible names for things throughout the industry. If everything used a name that made it easy to tell what it is, you'd run into the problem of everything either having way too similar of names or super long names being used to be distinguishable.
Also PyPy and PyPI are not pronounced the same (officially).
I don't understand your bit about the local names thing. What are you trying to import with that opening import that has the same name as the module?
1. Versions
- I like Python to take deprecation seriously. It is not a bug, it is a feature.
- If code is not maintained, you probably should not be using it anyway.
2. Installation
- Just use virtual environments. You have the venv module integrated in vanilla Python or you can use conda from the Anaconda distribution.
3. Syntax
- Every decent Python editor (including the most basic gedit) inserts 4 spaces when pressing the TAB key.
- Yes, you can use any kind of indentation you want: the rule is to be consistent. Either you always use TABs or spaces, just do not mix them.
4. Includes
- Have you seen the official Python documentation? The standard library is comprehensive and well documented. Yeah: you can import that.
- If the standard library doesn't meet up with your needs just ask. The Hitchhiker’s Guide to Python! is a great resource to community-developed packages.
5. Nomenclature
- I like Python nomenclature for native data types such as lists, bytes, strings, dictionaries, and so on. In fact, I would argue that the Python nomenclature makes more sense than those implementation-based technicalities.
- As for the horrible names in OSS, that is a feature: people can call their software whatever they like, and reinvent the wheel how many times they want. This freedom is what makes OSS a strong and diverse community, and also the reason why Python is growing so much.
6. Quirks
- Are you kidding? This quotation system is great! You can stick to "double quotes" if you want; nobody forces you otherwise, but I usually write strings with quotations, line returns, and utf8 symbols. This quotation system makes a lot easier to do that.
- As for the binary strings, that is because we live in the era of Unicode, were a character is NOT a byte. Separating implementation from concept makes string processing hell of a lot easier.
- As for the raw strings, just don't use it; they are totally optional: I'd love to see your regular expressions encumbered with double backlashes.
7. Pass By Object Reference
- Whenever you learn a new language you have to deal with the object model. In Python, objects are passed as reference, that's it!
- I like it because I have to deal with massive data objects and I don't want them to be copied over and over.
8. Local Names
- Same as before. Also, as I said in the beginning, Python is not C; I certainly don't want it to be C.
if True:
____print("It must be indented...");(
print("Really??"))
____print("Haha")
"""With C, you can just look in /usr/include/*.h. But with Python?"""
---
Sorry but I see you knows nothing about _import_ and the importlib. The python has much more tools to import than the PHP. Please read the following docs!
https://docs.python.org/3/library/functions.html#_import_
https://docs.python.org/3/library/importlib.html
Python3 versions are generally backwards compatible unless you depend on bugs or experimental new modules such as asyncio so not sure what you refer to there?
2. See #1
3. False. Python3 automatically validates inconsistent whitespace. You can't mix 3 and 4 spaces in the same file. In python 2 you can use the -tt command line flag to do this. You should also be running automatic code linter (pylint, flake8, pycharm, etc) which catches this as well. The official recommendation about tabs vs spaces is https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces which is very clear on recommending spaces so not sure why you claim it's ambigous which one to use.
Using flat indentation for debugging code is also very weird, most editors like Visual Studio for C# even auto indent the last block whenver you type semicolon or closing bracket.
Nesting so deep you run out of screen space sounds like a problem with your code and not the enforced indentation .
4. Imports running code is possible but most high quality modules don't do this and it's considered bad practice. Your comparision is also strange because PHP include works in exactly the same way as python, there you even have to create include guards unless you use include_once.
For finding modules and navigating in modules just install PyCharm and you get auto complete on all possible imports and can ctrl-click your way into them. People who grep through standard modules are not friends.
"import as" is supported in most languages, like TS, JS, C++, C#, etc... It can be abused and i rarely seen it be.
5. C# has Dictionary and List. Java has ArrayList. C++ has std::list. These are not strange names. And some third party modules have ugly names, so what, don't use them. Who searches and randomly installs packages based on names anyway? Regardless of language always google for "best library to do X in $PROGRAMMING_LANGUAGE" then go by reputation.
6. All of the differently quoted strings create the same object, there is nothing strange whatsoever about the == operator. Simply: "foo" == 'foo' == """foo""" == r"foo"
You NEVER have to cast a string with string(string) and the only time you need to decode() it is if you receive a raw byte-stream over network or open a file in binary mode. (actually in that case you are not decoding a string, you are decoding a bytestring)
b"-strings are different though, they are not strings, they are bytes.
7. C#, Java, PHP and Javascript all behave the exact same way as Python here. This is very normal behavior.
8. Valid complaint but i would say naming your module the same as another one is a bit strange. C has the same quirk btw. Not sure i see any obvious solution for it either.
I disagree in this point. The speed of a software is a part of a the high quality. So use it if you need it to avoid the too slow running time.
It's rare for 3.x and 3.y to be incompatible, as well as for 2.x to 2.y and 1.x to 1.y.
The changes from 2.7 to 3.0 were relatively large, but still not that big of a deal, and important for the long-term health of the language. The main change was that unicode became str, and str became bytes.
I choose to keep around many versions of Python for ease of comparison. It's not confusing at all. I build them using http://stromberg.dnsalias.org/~strombrg/cpythons/
Deep nesting is a mistake in any language.
Most text editors make it easy to use 4 spaces.
Having 3 spaces somewhere isn't a big problem, and a linter will likely catch it.
Adding debugging print's without indentation is an interesting idea. I just prefix all my debugging prints with "fred: ".
Finding a list of what can be imported is easy in a Python REPL: import foo; dir(foo) or help(foo)
To find what file an imported module comes from in a REPL: import foo; foo._file_
from foo import creates namespace conflicts. import foo does not. Do not use from foo import - it confuses linters too much.
Module names that aren't 100% clear are far from unique to Python. This is what Google is for, where a unique name is easier to search for.
Searching on Pypi is generally a pretty painless way to find modules related to a topic.
r'' is not for binary strings, it's for strings that don't do backslash escapes.
Yes, Python uses b'' for a byte string - like Rust.
Explicit type conversions are a defining aspect of strong typing.
Python is pass-by-assignment.
It's very much like pass by value, except that sometimes you pass a pointer by value.
If you change the pointer itself, nothing changes in the caller.
If you change what the pointer points at, of course things are changed in the caller.
This is nothing like using globals for everything.
Naming a script foo.py is poor practice for more than one reason.
One is, the nit you pointed out.
Another is, it unnecessarily ties your script to being forever implemented in Python, even if you decide it should be rewritten in Rust. Last I heard, Debian recommended against this. It supposedly originated in the Perl world, where Perl developers felt that CGI scripts should have the language they were written in, baked in.
Answer: Just don't.
It is broadly available, there is a large community, it often allows to express things in a beautiful way due to nice concepts. I am open for alternatives.
And you may not like the indents, but on the other hand i see it rather like a school programming language, if you dont type nice you get blamed. Thats great to achieve clean coding. Although i'm more a fan of C++ and C#, Python is great.
I love to go to kaggle.com write a few python lines and see how quickly you can do something with it.
Python jupyter notebooks i love them wish more coding languages had something alike it. (setting them up is easy too).
I am far from a python fanboy, and consider myself a true polyglot, but I have always felt if you try to write c in python you are gonna have a bad time, the same way is if you tried to write fortan in C or C++ in Cobol. If you stop trying to force the languages to your preexisting paradigms and allow your self to write the language in a way that is idiomatic I think you would find it much less frustrating. I find that python for scripting, web back end, business logic is among the more productive options available. Embedded or Systems programmers should maybe consider something else.
1. Imports, you barely scratched the surface on this. Python's import system is an absolute mess.
2. Threading. By default you get a single thread, and while some libraries like numpy are threaded rolling your own is a mess. The multiprocessing module seems to be the go to, but largely unmentioned in the docs is that it can only handle functions that can be pickled. Sure, there are workarounds but it makes anything threaded seem like an absolute hack.
3. pep8. Why should the language care how many spaces I put before a comment. At least this one's easy to fix.
4. The docs. They're fantastic for the basic topics, but rather lacking on more advanced stuff. On the same note, "advanced" in the title of any python tutorial seems to guarantee it will cover topics common to any second or third semester computer science course.
5. Reloading modified packages. This depends how an IDE is set up, but it's very easy to inadvertently end up with old versions of objects when packages are reloaded. The only sure way to know you have the latest is a complete restart of the interpreter.
6. Namespaces. Since import* is frowned on, code is full of the shorten abbreviations criticized in the article. Similarly, objects require 'self' to be used throughout. It's something you get used to eventually, but still annoying.
That being said, Python is still the best tool for most of what I'm working on right now. Numpy is a fantastic tool - especially if you're used to matlab's arrays. The flexibility of python is fantastic, but it certainly has some rough edges.
Thanks for the feedback.
I probably could have done a 12 item list, but I thought that 8 was enough. I had ruled out a few things, like performance. "Slow" and "memory hog" are relative. (Compared to bash, everything is fast. And try debugging the sudden delay from some languages randomly doing garbage collection while you're trying to capture all packets on a gigabit network.) I do think that #9 is documentation. The docs for PHP are absolutely fantastic. Python is good for basic concepts, but most third party libraries just suck at docs.
The whole thing about threading... I actually view this as a feature of the language (regardless of whether it sucks or not). They've had 30 years to implement real thread support. I just assumed that the language developers don't want it.
I've read some of the comments that people have left at Reddit and news.ycombinator.com. There's a lot of negative (expected), but a surprisingly large number of people agree with some (or all) of my gripes. Different items seem to strike a chord with different people. I get a kick out of the people who say things like "#2 is just whining" and someone else says "I agree with #2 -- pip is a mess!"
I also got a chuckle form the people who are so certain I'm wrong about broken backwards compatibility. (Does TensorFlow install under Python3.7 yet without building it from source? It works under 3.5 and 3.6, but 3.6 has performance and resource issues.)
https://github.com/tensorflow/tensorflow/issues/20517
https://github.com/tensorflow/tensorflow/issues/20444
https://github.com/tensorflow/tensorflow/issues/23478
A lot of people have suggested different IDEs for writing Python. But here's my issue: if the language really requires an IDE to be properly written, then shouldn't it include a default IDE? (As far as I can tell, every IDE is a third-party addition for working around the syntax limitations.)
And then there's all of the people who have suggested different virtual environments for keeping the different versions straight. Other people complained about how screwed up the virtual environments are. However, I think they're missing the point: why do I need to have separate environments for different projects? And maintaining different environments means more things to maintain. The whole need for virtual environments is just a workaround for Python's design limitations.
Wow, I sure went off on a tangent. Anyway, thanks for the feedback.
Python hates you back.
A lot of efforts just to slay a language; perhaps you should write one better. It sounds like you're afraid of something new.
Python was created in December 1989. That's nearly 30 years ago. (Technically, it's 29 years ago.) It's not "new".
If it were "new", I would give it a lot more leniency and consider the issues to be growing pains. Python is nearly 30 years old and it has no plans to move out of it's mother's basement.
My first encounter with Python was Anaconda, back in 2002. Horrible crap. No comments, space delimited, the whole insane "dictionary list" bs for a simple key-value hash. It gave me bad TCL flashbacks. Plus, the python2 vs python3 thing still drives me nuts. Also, why does it have to import a regex library? Perl comes with it built in,, FFS.
[quote]
Well, the original article wasn't all bad, but the author got a lot of things wrong about Python. I can understand getting frustrated about a thing and not wanting to pick it up and actually understand more because you're so frustrated with it, but I hope the author 1) reads this reply and 2) understands Python enough to gripe about the real problems with Python.
I'm totally fine with people disliking Python for the legitimate problems that Python has, or even because they really just like to have static binding on their variables (or even, like Rust, at least static binding until it's explicitly rebound to a new type) and they really hate that Python does that!
But I do get bothered when people dislike a thing and they're mostly or entirely wrong about the thing they dislike! That's just silly!
[/quote]
2. Stacktraces: totally unreadable.
3. Terrible regular expression library: take a look at Ruby and see how that can be smoothly integrated.
4. join syntax: Despite using Python day to day it still happens to me that I get this wrong. Why is join not a method of array-like types? No other language does it this way.
5. Iterations: not cleanly implemented and not a reasonable object hierarchy. See Java (!), Scala or Ruby how to design that properly.
6. List comprehensions: after 30 years SQL has invented CTE to read statements from left to right. Python goes the other way round and we must now read from right to left. Why not have map for all iterable types?
7. No functional style: why can't I call sort() on an array-like type and get the sorted array? The only option is to sort in place. Ancient.
The Python infrastructure and libraries are fantastic and code is highly optimized. The API though is often not elegant (pandas, numpy) and fosters more of a hacking attitude towards programming than a clean design.
I am always surprised that Python is praised as a fantastic entry-level programming language. I wonder if people supporting this have worked with languages which sport a really clean and consistent design. Although e.g. Ruby is much slower the design of the language AND of the standard library is so much better that it's really beyond comparison.
It is just that Python 3 evolves now and Python 2 in in maintenance mode.
You are also mixing different aspects: if apt get does not offer you the python version you want, than this is an issue of the linux distribution, not Python itself. So in your case Ubuntu sucks, not Python. And there are the mentioned tools like pyenv and virtualenv to handle Python installations pretty well.
The critics of syntax sound for me like "Python and its eco system is not c-ish enough so I don't like it."
You also should not try to compare compiled languages with a static type system to an interpreted language with a dynamic type system. In Python you can solve things more efficient than in C, but the other way around Python can perform bad for tasks where C shines.
I appreciate this discussion a lot, and I am aware Python has good and not so good features but you should try to separate criticism about the language from the interpreters and from the community and from linux distributions.
i start writing something in python sometimes because of reasons I resent afterwards, but i always switch to golang before finishing. it's worth it.
http://www.hackerfactor.com/blog/index.php?/archives/415-Open-Source-Sucks.html
Almost all modern languages support true multi-threading, but python cannot.
Don't talk about multi-process, that's another thing also supported by other languages.
I find it a fatal defect if a language lets me add properties/fields/member variables/functions/methods to an existing object. Because that way lies madness.
How am I supposed to know what an object can do? Check its type? Good luck with that if someone has souped the object up! Heck, python makes defining types optional to begin with.
Static type checking is vital to productivity. The earlier an error is caught in the development process, the less orders of magnitude it costs to fix it. Going from compile time to runtime is at least one order of magnitude.
It also makes it a pain to reason about the code programatically. Code completion gets ugly. Efficient implementation at runtime gets ugly. Remember shadow classes in javascript engines?
In short, duck typing was a mistake. Not just in python, but as a concept itself.
What does LWP ( Perl ) do?
What does capybara ( Ruby ) do?
What does Boost ( C++ ) do?
It's not immediately obvious what these packages do, from just the package name.
I am a PHP & JavaScript developer however I have long wanted to learn Python.
Of all the issues you mentioned what troubles me the most is the pass-by-object-referrence thing. Has anyone every responded to this criticism with a reasonable justification?
Try to do something trivial like have a pytest test script print a string
or "trace" the execution of a python script (i.e. in BASH "set -x")
Aaarggghhh !
It reminds me of some sort of braindead pseudocode "language" designed to "teach programming" to remedial-level grade school kids who will never write a line of real code in their lives.
Usually I can pick up a new language on the fly, find some example code, target sections of the docs, apply previous knowledge, make a few queries to stackexchange and I've got some sort of rough prototype working. No-go with python, still trying to figure out how to get something as stupid and simple as importing tkinter working - none of the examples I've come across in v2 or 3 actually work.
Python = garbage.
Python is typeless? News to me. Try doing "2" + 2. Works in Perl (maybe not with "strict", but I never used Perl) and PHP. Raises an exception (as it should) in Python.
(BTW, how did the parent comment not violate your CoC?)
Python is inconsistent when it comes to types. For example:
a="2"
b=1
c=a+b // value is "21" since it concatenates strings
print(c+4) // error
Python is definitely not a strongly typed language. But it's also not consistent as a dynamically typed language. It really resides in a no-man's land -- midway between dynamic and strongly typed.
Regarding the CoC: This topic brings out a lot of emotions. As long as he's only showing frustration toward the language (and not insulting people or swearing just for the sake of swearing), I see no problem.
Coming from a C# .Net environment, I have enjoyed Assemblies that are logical. Within those Assemblies there are Namespaces that further divide functionality. The objects within those namespaces conform to a naming convention that means that, without prior knowledge I can drill down to a method or property I have never used before and, through documentation or intellisence I can understand if it does what I want.
With Python I don’t have a clue. It’s like there was a meetup group for Python API naming conventions and someone slipped a load on purple microdots into the drinks.
Jupyre. I mean, wow, just grow the f*ck up. What the f*ck does a Jupyre do apart from getting you an F in spelling?
Have you used the SuperPonycorn-MangaLoveBunny Python Lib? It’s a set of DL libs that identity geographic locations that dogs will poo in.
Python can be as syntactically different as it wants, but it also needs to grow the hell up!
Anyway, since some of my friends like Python then over the years I have given a try to Python to get some stuff done also, but damn... there are thousands of libraries and none of them get you to the finish line without some hacks. There's too often some feature missing. It reminds me the phone app store, where you can find millions of apps for same purpose but to get things done you need to install at least 3 of them.
And then that duck typing. Like when working with lists. To get the length: len(list), to add element list.append(item). Whaaat??? Last time i was using len was in BASIC. And the '-'.join(list). It's not OOP approach where you would use noun-verb syntax. Another quirk: line endings. print() adds line break, file.write doesn't add.
I really appreciate the community effort put into Python but there must be some architecture and logic to make it a real deal. So far it's just a quick and dirty prototyping language... which keeps me wondering how could they do science with it.
Now I am hating it for its streaming XML parsing. There is nothing as nice as perl's XML::Twig or XML::Rules. SAX is too low level. Etree isn't much better. And as far as other XML/HTML parsing, I've found I may as well use xpath from lxml rather than BeautifulSoup.
Some of your criticism I can understand, however, there is one pretty gross misrepresentation by omission here, and that is Reason 7 (Pass By Object Reference).
What you don't mention here is that data types in Python can be mutable or immutable. Immutable values cannot ever be changed. What this means is that immutable function arguments will have semantics equivalent to calling by value. If you modify them, what you're actually doing is creating a new value and reassigning a pointer to point at the new value.
Immutable data types include all strings types, all basic numeric types, and even tuples (and quite a few more).
In practice, what this means is that you usually get exactly the semantics you want. If you are working with a basic data type, you get copy semantics, but only copy the value if you actually change it. If you have a complex and potentially large collection, most of the time you want to pass by reference - if not, making explicit that you're working with a copy is a very good idea anyway.
Also, you can get around some of this by using dict.update with your parameter as an input on a new dictionary ( `defaults = { "a":3, "b":4 }; defaults.update(argument); return defaults`).
Of course, this is a cycle that repeats every few years in the industry and like I mentioned is just delusional. Anyone with experience in programming knows full well that the solution to any non-trivial task will always involve a kernel of irreducible complexity that no language or technology will ever fully abstract away.
So of course at the end of the day every language and library implies unavoidable trade-offs between implementation speed, readability, performance, security and at least half a dozen other concerns that if could be tackled simultaneously, they would already have been long time ago.
I was neutral till I installed molecule and it threw a whole series of warnings about ~/.local/bin "not on PATH". OK, I'm a pedant, and the awful English was the initial spark, but that was blown into a raging inferno pretty quickly. Of course, just what we need, another (hidden - yes, really, hidden!) executables directory. I daren't write what I really think of the person who came up with that.
I can't understand why programming language designer, are creating languages without strong declaration rules.
Weak declaration rules, are the path to unreadable code.
If you have to work on someone else's code, or if you have to work with a lot of different programs, this can easily become a nightmare.
You see variables, but it isnt't easy to understand what those variables are being created for.
see this notation :
int Values[10];
The code has to help me, and remember me what "Values" is, and what this was created for.
And, if I try to put a string "foo" into Values, the code has to stop me from doing, because it's illogical to do so.
Unless I use an array of objects.
A good C, C++, C# programmer, really don't need Python....
"Welcome to Python! Here's an example: Lines of code are separated by whitespace . . ."
Nope, huh uh, I'm out. I can't take seriously any language where simply switching from one text editor to another could invisibly and drastically change the flow of the program.
I can't even comprehend commentary where "just use this IDE..." is a serious rebuttal to the un-detonated minefield that is using whitespace as syntax.
AFAIC, that's the end of the conversation re: Python's legitimacy. But, it takes a truly special feat of bad design to make the language painful to those who only wish to /consume/ pre-written code.
The constant version breakage, side-by-side installs, the ridiculous idea of sandboxing an interpreter, which is now practically a system component on any Linux distribution, so /your/ code can run on the version it wants, and /my/ code can run on the version it wants... Who needs that?
I've been using Perl for a long time, though not long enough to have even seen a Perl-4 script in the wild. So, from my vantage point, Perl-5 is the only Perl that has ever truly existed. Not once have I found code that wouldn't run due to incompatibility. (Missing lib? Sure.)
OTOH, I have also not once found a random bit of Python code that executed on the first try. Someone sent me a script once, which I tried running unsuccessfully on three different *nix-like OSes. The dev and I had four back-and-forth emails before I gave up and said, "This is ridiculous. I'm not installing an entire dev chain just to process one stupid text file. Why don't you just run it and send me the output? It would save us both the hassle."
I would be totally fine with the "don't like it? don't use it!" solution, except all the year-one university students have infiltrated the open-source ecosphere, and no matter how much a project says "write plugins / modules / components in any language you want!", they really mean "uh, just write it Python - there's like nobody here that knows how to interface with anything else."
Never spected such a basic standard to be a headache in Python but it did.
I've been a Python programmer for a few years and absolutely loved the language until I was (somewhat forcibly) introduced to Go.
Now, after doing some real-world tasks in Go, I can tell that Python is the worst tool for building server applications.
The way errors are handled, dynamic and weak typing, import mechanisms, ubiquitous inheritance over composition, you name it. I don't think I will return to Python again.
I don't think "ignorant" is the correct term. Python was designed for a specific purpose, but people are using it far beyond the original intent. (When all you have is a hammer, you beat that square peg until it fits into that round hole.)
E.g., I don't criticize python for lack of multitasking or semaphore support because that was never one of the design intents.
The same can be said for perl, ruby, lisp, etc. You don't typically see monolithic programs written in Perl because the language becomes unmaintainable. (There are very few examples where someone can immediately look at someone else's perl code and clearly understand what it means.) And languages like lisp, scheme, and fourth never really grew outside of a few niche markets.
With python, the community had a few people who liked the language and wrote some really nice libraries for it. Some of the libraries are awesome, but the language is not. Unfortunately, a few good libraries isn't a good reason to propagate a bad language.
I love Python... BUT, there are also some things in Python that I hate and are not mentioned here:
1. There is no keyword for declaring variables (such as var, let, const etc...)
You see, in Python, all you need to do is write the variable's name and assing a value to it. And that is a problem.
See the following example:
max_customers = 10
if user is admin:
max_customrs = 100
while customers_count < max_customers:
# do something
Do you see the typo? I just declared a new variable for the admin users instead of changing the value of
max_customers (which is what I wanted to do).
This is easy to spot right now, but imagine that being the case in a large code-base. Good luck debugging that!
2. The OOP sucks. If you want to declare a private method in a class then it must start with underscores. Not only
this feels like a quick and dirty fix for not having real supprort for private methods, but also the "private"
methods are still visible AND usable from outsiders. And sadly this is something that is not going to be fixed.
Let me say this about spyder : if u want to go line by line the position of the executed line remains the same on the screen and the code scrolls up !!!
Python is only good because is free.
I used to love python but nowdays I'm quickly falling out of it.
After moving to more than simple scripts the drawbacks of the language bog you down.And the worst part is that the inherent drawbacks are supposed to be treated by huge "legalese" lists of "best practices" and cargo cult beliefs. All the things that made python appealing to me -namely being able to be productive without the language inhibiting me- seem to pedal backwards as the language and the community grows.
Enough is enough.
What is wrong with the standard c printf?
Why do we need the percent delimiter in
print("%s"% var);
what the hell is wrong with commas?
What was wrong with the simple \n for a newline? Why do we need some (end = "") expression?
Why do we have to waste our time with useless syntax?
Will it help to save the environment? NO!
Will it make SW developement easier? NO!
It's just WOMBAT!
Waste
Of
Money
Brains and
Time
What's wrong with print ?
> Why do we need the percent delimiter in
print("%s"% var);
It tells the parser that the formatting applies to the screen memory - they had this on the ZX81 !
> what the hell is wrong with commas?
The comma puts the characters into columns.
> What was wrong with the simple \n for a newline? Why do we need some (end = "") expression?
They are two different things, the \n induces the carriage return, while end="" supresses the carriage return. The ; was unavailable because it's used to break lines instead of the : which is used to denote conditional statements. (Newline also is on the ZX81).
Let me tell you that I HATE Python! It contradicts with whatever I've learned in programming in the past 25 years. Developing a project on the enterprise level is a pain in the A@# .. trying to maintain, debug and enhance code is a serious effort. Yet, big companies like Google, Amazon and Microsoft are all endorsing Python. It's like I have to give up whatever programming principles I've learned over the years and use this crapy language. Think of building a model using LEGO, with solid, consistent, clear building blocks that you can use to reshape whatever you want and compare that to building the same model with clay or play doh. The model will standout with LEGO even if it goes higher and more complicated but with the play doh it becomes messy, hard to maintain or keep standing and it might collapse or deforms in terms of shape.
Yes, there is a great flexibility with Python but that comes at a very expensive price. In short, I HATE IT.
Thank you for a wonderful blog.
I was trying to read data from a csv and using the type() function to figure out what to do with them. Python said 40000 was type str. Really? Python's dynamic typing means it's impossible for me to know what I'm reading in from external files. I was forced to use a series of try loops to convert the input to float and then use process of elimination to figure out what the entries were. It is a shitty language that forces me to use try loops to figure out what something is.
The other day I had to use a 2D array with only 1 column for a particular application (the library I was using expected a 2D array even though my data was 1D). I told python to check for nan values, and it changed my 2D array to 1D without telling me! It took me a long time to figure out what it had done.
I hate python. I wish they would make some libraries in C for the user-friendliness, and then everybody could switch over to C and never touch python again.
Python is also slow.
> "I told python to check for nan values, and it changed my 2D array to 1D without telling me!"
These both sound like issues with Pandas, not Python.
Back when I was running OS/2, I absolutely loved REXX. OS/2 with REXX can still do things today that you can't do with Windows, MacOS, or Linux. (An object oriented language tied to an object oriented OS.)
And I agree: the sample code and documentation for Python is seriously lacking.
Python does not have dynamic typing. All it does is allocate the type based on the first character, then the variable is static. It just doesn't have a declaration like Pascal.
You would have to use a FOR loop to read in the array anyway and float can be included in that.
> The other day I had to use a 2D array with only 1 column for a particular application (the library I was using expected a 2D array even though my data was 1D). I told python to check for nan values, and it changed my 2D array to 1D without telling me! It took me a long time to figure out what it had done.
Sounds like what you did was read in data in a single un-nested loop, thereby creating a one dimensional array.
The worst is the "feature" that you can create an instance variable from outside the class - and apparently there is no way to keep it from happening (unless you use some internal hasattr/setattr functions). This totally breaks my idea of a class as a model - if you can add to the model without even meaning to add to it (so someone could make a typo and it will become a new attribute?)
Ami
Language seems great but it’s messy, and it has zero good abstractions (decorators are trash) that actually let people write good, maintainable code. Dependency management is terrible (pip is garbage; and to anyone who says “just use a virtualenv”, you don’t get that virtualenvs are only required BECAUSE dep management is trash - Ruby and Rust don’t require envs), the std lib is inconsistent in quality, and the ecosystem in general is just... frustrating. Most, like you say, are comically poorly documented, or are so overthought that they are more complex and tedious to use than not (looking at you, sqlalchemy).
Half of the issue with Python is that Python has rabid fans that get offended if you say anything against it or the packages they maintain.
You get the picture - I've picked up and put down many languages over those 50 years.
I have taught Computer Science, and have worked on many major projects, including safety critical and financially critical.
I realise that languages are useful in different contexts, but the one that I hate with a passion is Python. It seems to be have been the result of a teenage unxoid's wet dream - something to fill an ego not a need.
When I first came across Unix in the '70s it was a bag of shite, yet Unixoids sang its praises, citing its weaknesses as its strengths. This is the way of Python.
Some people may be lucky enough to have to code in one language, and a language in which they have been trained, but most of us have to chop and change, and learn the new language as we try to code with it productively. How many of you, in those circumstances, enter code afresh? And how many find an example and cut and paste it? I am often in the latter camp with a new language (especially now that my ageing fingers have resiled useful action).
The use of spaces to define scope is a nightmare. I can't just cut and paste Python code - I have to work out at what level of nesting I am operating, and add spaces. If I am using a language that uses begin, end or {,} I can ignore indentation, which is a cosmetic aid to maintenance, and just test the pasted code. The cosmetics follow. With Python I have to spend too much time tapping in spaces that I'd prefer another prostate investigation.
A space on a screen is invisible - it is the lack of a character. How many lacks is it? How am I supposed to count a space? Comparison with the line above or below? What if those characters are not countable either? This is as bonkers as those who use underlines/underscores which, on many printers and display systems, come out as an unbroken line so are uncountable. Or (another Unixoid practical joke) makefiles where tabs and spaces may have different effects and meanings.
I await the next undergraduate computer science student project language to force its way into the world like the creature in Alien.
How can Python claim to be "more readable" and "easier to understand" when the loop statement "for value in range(1, 5):" EXCLUDES the upper limit! And why does the loop statement require a colon at the end of the line when a newline is fine for all others?
Scope rules... OMG! White space is better than brackets? As others have mentioned using whitespace leads to all sorts of problems especially when you're quickly trying to prototype and debug. Another good point was made about inserting other code. Why can't it be left to the IDE to make the code look pretty? Python seems to make it HARDER to code by making the programmer do work that an IDE can do.
I was shocked to learn from this article that "Saying "a=b" just assigns another name to the same object space". THIS IS SIMPLY NUTS! How is this even useful? Have all the other programming languages I've come across been doing it wrong all this time?
Anyway, I'll continue to learn Python, but I'm already starting to feel that what I'll end up learning is that "the one that I hate with a passion is Python"... come to think of it maybe the joke's on us (Monty Python)...
Python doesn't need whitespace or brackets. The parser ignores both of them. When it encounters a command it just uses the next characters to feed into the link code.
The whitespace is only there to make the code easier to read.
Youy could write the whole program in a single stream-of-consciousness style block without any spaces like on PET BASIC and the parser wouldn't have any problem understanding it, if it hadn't been deliberately written so that coding conventions were followed.
> How can Python claim to be "more readable" and "easier to understand" when the loop statement "for value in range(1, 5):" EXCLUDES the upper limit! And why does the loop statement require a colon at the end of the line when a newline is fine for all others?
The colon tells the parser that the folowing code is a loop, in the same way that the command "range" tells it to expect up to three variables - the brackets are irrelevant and are completely ignored by the parser. They are just there to make it more readable.
Range, which has been removed for Python 3, doesn't use the upper limit because of the way the link code was written ; its a loop that starts at 0 and stops and exits once it reaches the stop value.
You are correct that you don't need linebreaks. You could write an entire program on a single line, but nobody would do that. Any program requiring more than a dozen commands would be unreadable and unmaintainable.
You are incorrect about python ignoring white space. Python absolutely uses initial whitespace to determine scope.
We were given a fairly simple script to create (in a Windows 10 environment) that had some user input of filenames. We weren't expected to be able to handle directories, but of course I had to make things difficult for myself and accept any kind of legal path (absolute or relative, including network paths, handle hard/soft links and directory parse points, ".", "..", ugly stuff such as "dir////\\\dir", and alternate data streams) and cleaning up what was entered into a canonical form.
The path processing is woefully inadequate, almost garbage. My best guess is that it was written for *nix-like filesystems and nobody cares if the Windows version is broken. So broken.
I think I had numerous special case checks to see if what was entered was a directory (which involved checking the original string as well as the path parts, because the path library/object manages to throw away some critical information!), then, starting with the current directory, appended the path parts one at a time and checked to see if the result (partial path) was valid.
The result was pretty nice in that it would tell the user where it was able to follow the path to before some typo/error, and also that it reported a canonical path for convoluted symlinks, going up and back down levels ("dir1\..\dir1\file" -> "dir1\file").
I'm sure it was still broken for some edge cases, and it was painful.
Oh, and there appears to be almost zero support for Windows security, which is kind of a big deal (detecting if the thing failed due to a permissions violation seemed an impossible task).
One weird thing I discovered through this process is that you can hide data in a directory. Not a hidden file in a directory, an alternate data stream attached to the directory itself.
eg C:\temp:secretdata
Re cybersecurity: it seems that Python is the lingua franca for the ecosystem of vulnerability testing. If you weren't scared before, you will be...
pathlib library with its abstraction layer simplifies it greatly.
for validation and sanitization - I recommend https://github.com/thombashi/pathvalidate
With those two libs, dealing with paths in Python is pleasant! Even remote drive paths, converting between relatative, absolute, resolving symlinks ect.
But - you have to understand at least some basic Windows low level concepts and differences with unix paths. Probably .NET has better support for that
If you want to deal with window file system internals, permission issues ect. - understand and use Windows API. You can use it with pywin32 library.
Like I said, I've used Python on smaller functional programs. Now I started to look into AI. Noticed that everything seems to run with Python. And also, how the issues noted in the op are true - Python is / seems to be an obscure language, prone to error and difficult to debug. That scoping, name mangling! So, I am doomed.
What's the ** point of making AI/ML use only Python **!? I need to find working solutions for AI/ML using a sane language environment. Sorry for the rant, I'll see myself out.
But I don't like Python too, for all same reasons. Also because no semicolons.
The stupidity of using indentation as the sole method of determining scope is enough (all by itself) to make it clear that Python is a poorly designed language.
The other points are also excellent proof, though some might be considered arguable (by others, but not by me).
The only thing worse than learning Python would be having someone learn Python as their very first programming language. It is an utterly bastardized chunk of Bantha Poodoo disguised as a programming language.
Having it be a person’s introduction into the world of programming would cause an immediate (and completely justifiable) dearth in people entering the field.
Just my opinion, but I have to say that none of the clarifications in the comments have even remotely changed my mind. If anything they seem more like justifications than explanations.
(and yeah, I know my handle is silly).
Unfortunately, the problems noted by the author are very real and still around.
I started up a simple Python project tonight and went to get some helpful examples from the large community.
None of them worked. First, the syntax was unsupported, so I had to fix those (mostly print). Second, the Python library I was trying to use (a core library) changed how it works and none of the examples passed the correct type anymore.
The examples would no doubt work on an earlier Python 3 or Python 2. But examples typically don't warn you about versions. They don't expect their script to stop working next year.
So, now I'm questioning if I even want to bother with this.
As a life-long C/C++ programmer, this aspect of Python just gives me hives.
This is another language "feature" which makes it super hard to comprehend Python.
And honestly, any language which requires you to use virtual environments to deal with compatibility issues is one which is fundamentally fubar.
It is hands down the worst language I have worked with. Even just for simple tasks like pipeline orchestration it is hideous. And the version hell and virtual env and modules are ridiculous. It just looks and feels and acts like something hacked together for decades.
Just tonight I pulled a git repo down, spun up a venv, pip installed, ran into to wheel errors, worked it out after a while, ran the pipeline and failed due to module not found. This is a public repo for a major data orchestration provider.
So I decided to quit this bullshit and go back to being a real engineer. Gonna put in my notice this week. Python may be the worst thing to happen to my professional career. Y’all can have it, I’m out.
Tkinter isn't a screen, its just an interface. It just displays switches that alter the running of the program, you can't alter elements of the screen dynamically or make them recognise each other.
There are different packages, but once a developer gets bored with them, they become unusable in later Python versions and you can't use them, any code wriiten in that beconmes an orphan (to be fair, this is a problem with C as well, limited to some extent by the fact that that is fully compiled). Wither PyGame ? Or Kivy ?
2. There is no GET/INKEY$ function, all typed in information must be followed by enter. This was unaccceptable in the 1970s. I can understand this limitation in the text buffer, thats just for debugging, but its totally unacceptable in tkinter.
3. The documentation is incomplete, it doesn't mention the switches for functions/classes for example like text="", or anchor=TOP, and I can't find any reference to them anywhere, certainly not in Python.org. What are the different fonts ? Can you set a global font instead of having to declare it every time ? Who knows ?? What the hell does "kw" mean ?
4. There is no consistency in terms, switches are sometimes called options, functions are classes, the difference between packages, classes and sub-classes is unclear and differs in different parts of the documentation.
5. The instruction websites are very poor, examples are short, run once and then crash back to the line editor and are completely out of context, routines are given and not explained you just have to kearn them by rote, the text buffer is incorrectly referred to as a screen, the functions are not documented and object code, which is fundamental to the operation of all but the most trivial programs is buried at the end of the text and only refers to making classes when mostly you need to know it to use classes you import.
6. For some reason, ncurses is not included in Windows. So you are forced to use tkinter and slowly and painfully render an interface through two different window layers. Is a Windows binary of ncurses so emotionally difficult to come to terms with for Python.org ?
list.append(1)
list=[0]
append(list)
print list
--
Python stores variables - and everything else as strings.
What is a string ? Its just a series of memory cells.
So, if you say a="now", it will record n then o then w at the pointer in memory where the variable starts, say location 16128, 16129 etc.
The variable name is just that - a name for the string. Assigning a new name say b=a, simply adds another name to the string, it doesn't make another one.
A method is like a "magic box" - you put one or two strings in it, turn the handle and a result comes out and is stored back in the original string. So what you do is make a new variable.
list1=list.append(1)
then
print list1
Every time you use a method, you need to assign a variable to it, because methods do not create space to put output.
Python makes it easy to do a lot of stuff. The syntax has some quirks - OK, a lot of stuff - but it lets you be as lightweight or heavyweight as you want - as the problem needs. You can build a great, reusable library, with complete object-orientation enabling edit-time error checking and IDE goodies, or just dash off a 10-line script that graphs a whole linear regression. The beautiful library isn't significantly harder than in OO-centric languages like Java (I spent years with Java, other differences, OK).
It's true that Other People's Code can be an issue. So it is with any language. Languages that make it hard to write unreadable code also make it hard to write simple, non-reusable code.
So Python is very flexible; I have come to appreciate its value. That said, I agree with EVERY ONE of your criticisms, and the use of indentation as scope is a perfect example of a tradeoff that's Just Not Worth It.
There aren't any arrays in Python. There are only string lists.
> And an associative array is sometimes called a 'hash' (Perl), but Python calls it a 'dictionary'.
Its not an array, its linked string lists.
> Python defaults to doing functions with pass-by-object-reference parameters. This means that changing the source variable may end up changing the value.
Um no. Functions are procedural code and variables are local. What you mean is CLASSES.
a=maths.sin(x)
In object code, a class takes a parameter x and applies a method to it, then returns the result back to the parameter x. Then it makes a new name for the namespace x called a - there is ONE namespace and TWO names.
a=sin(x)
In procedural code, Python takes a parameter x and applies an operator to it, then puts the result on a "stack" and then moves it from the stack to "another" namespace which has the name a. There are TWO namespaces and TWO names.
It's really a nightmare to say python suck!!
Also, I love how in the comments people say "use pyenv".
Also, I love how in the comments people say "use pyvenv".
Also, I love how in the comments people say "use virtualenv".
Also, I love how they slowly and sadly bury themselves when they (hopefully) realize that the same problem it fixes, is created once again.
Hmm, I wonder what happened to Perl.