Skip to content

Instantly share code, notes, and snippets.

@RobertAKARobin
Last active November 17, 2024 21:07
Show Gist options
  • Save RobertAKARobin/a1cba47d62c009a378121398cc5477ea to your computer and use it in GitHub Desktop.
Save RobertAKARobin/a1cba47d62c009a378121398cc5477ea to your computer and use it in GitHub Desktop.
Python Is Not A Great Programming Language

Python is not a great programming language.

It's great for beginners. Then it turns into a mess.

What's good

  • A huge ecosystem of good third-party libraries.
  • Named arguments.
  • Multiple inheritance.

What should be good

  • 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.

What's "meh"

  • 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.

What's bad

  • 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 a map object, which is pretty much useless since it's missing append, reverse, etc. So, you always have to wrap it in list(), 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 a freeze or immutable method. 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 npm has its warts, it is widely accepted that a fresh environment should be set up with npm i && npm run [script]. Meanwhile each Python project seems to require a unique mish-mash of pip and pipenv and venv and other shell commands.

What's bad about the culture

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.

@bakoontz2
Copy link

bakoontz2 commented Aug 9, 2024

@dtonhofer Python is just a worse Perl.

Amen. For grins, I spent 6 hours today writing a "simple" python script that parses a file, selects some matching lines using regex, categorizes them into two lists, combines them under certain conditions, then greps another list generated from another file for matches.

And then I wrote it in perl. In under 30 minutes.

Sure, I've been using perl since the pink camel book. But even if I subtract out the time spent doing Google searches for silly stuff like "how to create a class instance variable in python" (in a constructor, silly!) and setting many, many print statements to figure out why a two-nested-group regex match is returned as a list of tuples (never mind the fact that the list itself is mutable), and that python "re" doesn't support multiple group matches (but python "regex" does, but good luck Googling that, because you get all the regex hits for "re"), I still spent about 3 hours on it. It shouldn't be this difficult and arcane.

And call me old-school, but I'm pretty certain that data encapsulation is a necessary component of any language that claims to be object-oriented. Which makes python not an object-oriented language, despite what Pythonists want you to believe (I believe this quote sums up the iron-clad reason why python doesn't include encapsulation: "After all, we're all consenting adults here." )

@nilslindemann
Copy link

nilslindemann commented Nov 17, 2024

What's Good

I agree to these points and could add many more.

What should be good

That's a bit blurry, so I can not criticize or agree.

What's "meh"*

Forced Indentation
Okay, that can create problems, like indentation getting lost when copying from the Internet. That happened once to me since I use Python. In practice, it is no problem.

Dynamic typing.
What are you missing in today's Python type system?

To the above I would add:

Verbose syntax for dataclasses

One needs to write "class" four times before using the first data. Python needs a data keyword to create data (structs in C, I guess), which would be close to JS expressiveness.

No let block defining space for temp variables

Python does not have that:

let foo, bar:
    baz = 3
    foo = baz * 2
    bar = baz * 5
# foo is 6, bar is 15, baz is not known.

in JS, I can write:

let foo, bar; {
    let baz = 3;
    foo = baz * 2;
    bar = baz * 5;
}
// foo is 6, bar is 15, baz is not known.

No section blocks

"A description of what follows. You can fold this in your editor":
    # here code

What's bad

400 ways (more or less) to interpolate strings
I also dislike using "%" for string interpolation. I use f-strings, if possible. The .format(...) syntax is needed when the format string is inside a variable. That makes one obvious way to do it for every use case and one way, probably kept for backwards compatibility. Not too bad.

69 top-level functions
Why do you need to memorize them? Your IDE has autocomplete, you can print out a cheat sheet, or keep that site open as a pinned tab. Also, your brain has memorized thousands of words in your mother tongue and other languages. When you use Python daily, you will soon have memorized these little lib function names too. I never have to search for them, working is very productive. list, set, dict, print, try to memorize their equivalents and their usage in Java or other languages.

map doesn't return a list
And that makes sense. If you want to map thirty functions over the list you don't want to create a new list in memory each time. And is wrapping in list(...) really so ugly? It is explicit and short. I not just got used to it, I started to like it. Same with generators, iterators, context managers, ...

[[row[i] for row in matrix] for i in range(4)])
Whatever you are trying to do here, you can and probably should write

for i in range(4):
    for row in matrix:
        mylist.append(row[i]) 

The next point is the same, these shortcuts should not be used when the task is complex. Use a normal if / for block statement.

Syntax for tuples
It is a short syntax, but I find it intuitive. I have never run into that error in practice, as far as I remember. How often did you use a one element tuple? And how often have you run into that specific error and were not able to quickly find its cause? Some things just have a short syntax, and it does not hurt. Would you complain about string literals being wrapped in just " and "? I guess no, because you are used to it. (for the records, you don't even need the brackets, you can write mytuple = 1, and I like this!)

Frozen dict, frozen objects
For a frozen dict, create a subclass and overwrite __setitem__. There are libs for other frozen objects. But for what do you need frozen things? I never did.

Regular expressions have a verbose syntax.
Accepted, JavaScript is better in object access syntax and simple regexes. But try to write a complex regex in JS and you will experience pain, while in Python you have re.VERBOSE. Ok, I once figured out a smart workaround for Javascript.

Goofy string syntax
Same as with the tuple syntax, it is often used, and I like that it is short. What would be a non-goofy syntax in your opinion?

Magic underscore syntax
I think it is very expressive. I also just have __init__ __name__, __main__, __slots__ and __pycache__ in my memory. For everything else a Google search or print(dir(<thing>)) or print(help(<thing>)). 90% of these I have never used. You do not need to memorize them.

No Exception catch-all

I do not understand.

try:
    ...
except:
    ...  # Voila, nearly all errors catched - including those you never intended to.

More info.

Many dev environments

Yeah, I did not like that too. But today everybody seems to use venv or no virtual environment, it has gotten better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment