It's great for beginners. Then it turns into a mess.
- A huge ecosystem of good third-party libraries.
- Named arguments.
- Multiple inheritance.
- It's easy to learn and read. However, it's only easy to learn and read at the start. Once you get past "Hello world" Python can get really ugly and counterintuitive.
- The Pythonic philosophy that "There should be one -- and preferably only one -- obvious way to do it." As someone who loves working within rules and rigid frameworks, I love this philosophy! As someone who writes Python, I really wish Python actually stuck to this philosophy. See below.
- Forced indentation. Some love it because it enforces consistency and a degree of readability. Some hate it because they think it enforces the wrong consistency. To each their own.
- Dynamic typing. There are lots of dynamically-typed languages and lots of statically-typed languages. Which kind of typing is better isn't a Python debate, it's a general programming debate.
-
400 ways (more or less) to interpolate strings. This prints "Hello Robin!" 3 times:
user = {'name': "Robin"} print(f"Hello {user['name']}!") print("Hello {name}!".format(**user)) print("Hello %(name)s!" % user)
If there was a unique and obvious use-case for each of these then that would be one thing, but there's not.
-
69 top-level functions that you have to just memorize. GvR's explanation sounds nice, but in reality it makes things confusing.
-
map
doesn't return a list, even though the whole point of a mapping function is to create one list from another. Instead it returns amap
object, which is pretty much useless since it's missingappend
,reverse
, etc. So, you always have to wrap it inlist()
, or use a list comprehension, which, speaking of... -
List comprehensions are held up as an excellent recent-ish addition to Python. People say they're readable. That's true for simple examples (e.g.
[x**2 for x in range(10)]
) but horribly untrue for slightly more complex examples (e.g.[[row[i] for row in matrix] for i in range(4)]
). I chalk this up to... -
Weird ordering in ternary/one-line expressions. Most languages follow a consistent order where first you declare conditions, then you do stuff based the on those conditions:
if user.isSignedIn then user.greet else error
for user in signedInUsers do user.greet
Python does this in the opposite order:
user.greet if user.isSignedIn else error
[user.greet for user in signedInUsers]
This is fine for simple examples. It's bad for more complex logic because you have to first find the middle of the expression before you can really understand what you're reading.
-
Syntax for tuples. If you write a single-item tuple
(tuple,)
but forget the trailing comma, it's no longer a tuple but an expression. This is a really easy mistake to make. Considering the only difference between tuples and lists is mutability, it would make much more sense to use the same syntax[syntax]
as lists, which does not require a trailing comma, and add afreeze
orimmutable
method. Speaking of... -
There's no way to make
dict
s or complex objects immutable. -
Regular expressions require a lot of boilerplate:
re.compile(r"regex", re.I | re.M)
Compared to JavaScript or Ruby:
/regex/ig
-
The goofy string literal syntaxes:
f''
,u''
,b''
,r''
. -
The many "magic" __double-underscore__ attributes that you just have to memorize.
-
You can't reliably catch all errors and their messages in one statement. Instead you have to use something like
sys.exc_info()[0]
. You shouldn't have a catch-all in production of course, but in development it's very useful, so this unintuitive extra step is annoying. -
Dev environments. Setting up an environment is a problem in any langauge, but other languages have solved the problem better than Python. For example, while
npm
has its warts, it is widely accepted that a fresh environment should be set up withnpm i && npm run [script]
. Meanwhile each Python project seems to require a unique mish-mash ofpip
andpipenv
andvenv
and other shell commands.
Most programmers will acknowledge criticisms of their favorite language. Instead, Pythonists will say, "You just don't understand Python."
Most programmers will say a piece of code is bad if it's inefficient or hard to read. Pythonists will say a piece of code is bad if "it isn't Pythonic enough." This is about as helpful as someone saying your taste in music is bad because "it isn't cultured enough."
Pythonists have a bit of a superiority complex.
I've been running across Python enough in my work that I thought it best to learn the language well enough to understand what I'm reading. As I always do when learning a new language, I took a recent (small) project and rewrote it in the new language as I went along.
For reference, I'm old enough that yelling "Get off my lawn!" is totally appropriate but not old enough that my belt buckle is hiked up to my belly button. I grew up on BASIC in various flavours as a teenager, then Turbo Pascal in first year university, followed by C, Fortran, APL (a language invented on 1970s-grade psychedelics if there ever was one), Prolog (1980s-grade psychedelics), and assembly. I was an early adopter of C++ and later Java. I've dabbled in various custom programming languages for databases, I know my way around VBA for Excel automation, I've developed applications solo for Fortune 500 companies that have measurably saved millions of dollars a year, and I've led teams developing applications for broad commercial use.
Oh. Dear. God.
No intrinsic support for constants. White space (indentation and line breaks) as mandatory syntax. Class instance attributes declared in constructors. Class static attributes declared outside constructors. Private names (methods, classes, attributes) managed by the underscore convention. Static class initializers through custom annotations. Typing declared as "x: int" for variables and "x() -> int" for methods. Required use of "self" because scope detection is too hard. Duck typing, so that a variable can end up as a type other than the type declared for it, leaving error detection to runtime. Wrapper types around everything so that even basic integer manipulation is tens to hundreds of times slower, and a numerical library (NumPy) that is mysteriously even slower when trying to do something with those same integers.
I understand the need for simple languages to do simple tasks (VBA is a case in point). And if that's all Python was confined to, I wouldn't be having to learn it unless I found myself in an environment where it was the only choice. But, if a language is going to be used for something mission-critical, it had better be good enough to protect me from myself. I will make mistakes; any programmer who tells you they don't (I've worked with a few) is just not good enough to recognize their own limitations and is not someone you want on your team. Many mistakes are easy and boil down to simple coding errors (highlighted by a good IDE before the code is even compiled) or misuse of a complex library (RTFM, assuming there is one). Logic errors are, of course, harder, and require comprehensive testing, and what I don't want is to go through logic testing and get sidetracked by a duck typing bug that a sane language would have caught at compile time with a syntax error or at assignment time with a typecast exception.
Python is what you get when someone looks at the landscape of languages and says to themselves, "You know what? We need a simpler language for simpler tasks." and watches in horror as the language gets adopted in ways for which it is totally unsuitable and never intended. And then, to address the problems that invariably come with languages that don't protect programmers from themselves, a whole community rises up to address the shortcomings and we end up with a language that is neither as simple as intended nor as fit for purpose as far more mature languages. It's the cripple and the tailor joke come to life.
Get off my lawn.