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.
-
mapdoesn't return a list, even though the whole point of a mapping function is to create one list from another. Instead it returns amapobject, 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 errorfor user in signedInUsers do user.greetPython 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 afreezeorimmutablemethod. Speaking of... -
There's no way to make
dicts 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
npmhas 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 ofpipandpipenvandvenvand 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.
@Benzor94 f-strings are simple for basic usecase, but beyond that? It's a whole mini-language with too many quirks to remember. Most users, including myself, forget beyond just basic string substitution. Better to be verbose than esoteric. At this date Python has
printf-style formatting,string.Template,format(),str.format(), f-strings, and t-strings for string interpolation. What did Python's zen say again? Ah, "There should be one-- and preferably only one --obvious way to do it". Sure buddy, it's all obvious which one to use, innit?loggingstill uses that very style of formatting. Even within the stdlib they couldn't arrive at a consensus. What consistency can you expect from a language like that?And, that's better.
max(),min(),any(),all()should be iterator methods that yield another iterator. One of the disappointing bits of Python is the lack of method chaining for iterators. What could have been this:is forced to be this nonsense:
And, yes, it would make more sense for
powto be in themathmodule; because it's a mathematical operation.Python's a duck-typed language, mind you. The excuse of “but it’s a protocol” isn’t justified. The protocol just could have been defined differently; e.g. if an object supports advertising it’s length it could do so using the
.lengthproperty or.length()or.len()method. And, it would work because Python’s a duck-typed language.I'm sure it's called function overloading, because those aren't operators—neither technically nor practically.
There are custom types that mimic
dict, but aren’tdict. But to retain compatibility, you use duck-typing there. You’ve sabotaged your own argument here.Not really, sometimes you just don’t want to collect into a container. And, I’d argue that method-chaining is far more readable than any comprehension. Regarding my previous example, here’s a comprehension-based version.
Can you call this readable? Because I just can’t; it’s like trying to apply makeup on an ugly beast. And, besides a
filterMapoperation, comprehensions aren’t all that powerful to begin with, as you yourself have stated; it’s conciseness is deceptive—good for simple cases, but horrible for anything more than that.And speaking of verbosity, it’s good being verbose, than being a edgelord who speaks in oneliners.