It’s time that I got a bit more real with you guys. Pretty much ever since I learned Python, I’ve been touting it as a super amazing language. I’ve been doing the same with Kotlin, but this is about Python.
Now, this doesn’t mean I’m going to be changing my tune from here on out; after this, I’m not really going to bringing up the weak points of Python. I’m just going to continue talking about strong principles, practices, features, and tips and tricks. But this article was just inspired by a small revelation, and I decided to just roll with it.
That inspiration was due to the revelation that the official Python logo looks vaguely like a yin yang symbol (apparently, it’s called the Taijitu). To prove it, I made an alteration to the symbol, changing just the colors.
So, let’s look at the pros and cons of Python.
Dynamic and duck typing is awesome. It can give you so much more freedom than a statically typed system. But it’s not perfect. Like a lot of features that place limitations on what you can write, giving freedom goes hand in hand with allowing dangerous mistakes. For example, using a garbage collection system instead of manually creating and deleting objects restricts you from being able to manipulate pointers in other ways (which can actually lead to worse problems that forgetting to clean up your objects).
In this case, by not restricting what types can go where, it becomes easier to pass in/out an unexpected type that causes the system to fail. Because of this, it’s often recommended to do more extensive of testing to be certain that usage of the code is correct and the code itself does what it should.
I myself have had to deal with issues of dynamic typing in testing because I like to work top-down, and changes to lower types can cause tests to fail almost inexplicably. If there was compile-time type checking, it’d be easier to know that these tests don’t work anymore and why.
To its credit though, Python has added type annotations, which allow for static analysers to at least give warnings, and mypy is being created to add optional static type checking to python where it’s wanted.
Everything is “Public”
This is a lot like dynamic typing. In a lot of cases, it just makes things easier to do, and that definitely applies to keeping the code cleaner (no need for visibility modifier keywords), but it also makes it easier for problems to crop up. My biggest reason for liking this is simply the lack of needing to think about what it should be. Sometimes, though, I prefer to keep my details more hidden.
Oh So Simple
Easily my favorite thing about Python is that lack of syntactic overload. There are very few keywords that get in the way, and most special characters are only used in small situations, leaving the code filled mostly with English (assuming you name your variables well) and Math, which are very familiar to us. Forcing “tab” delineation makes it so that you don’t have lines wasted on opening and closing braces (or similar) or keywords. The use of short-word keywords as operators instead of using full method-call syntax (I’m look at you, “in”, which is also used well in for loops and comprehensions) makes some things just so much easier to read.
What’s crazy about the simplicity is the amount of power that is still available because or despite it.
The only problem with such a simple syntax is that it has forced lambdas to be single-line expressions, unless Python decides to use a syntax that simply isn’t Pythonic.
I’ve talked about this before, the geniusness of having
self be explicitly in the parameter list, but it clearly comes with a downside. I say “clearly” because I’m pretty sure there are people out there complaining quite loudly about it. Having
this be implicit is generally preferred, allowing you to 1) not need to use
this in a reference when it’s obvious, and 2) not need it cluttering up the parameter list.
Mostly, when it comes to a language that allows you to just absently assign normal functions as methods on a class and vice versa, it’s good to know where
self comes from/goes to. Kotlin has to have some interesting workarounds with their reflection classes in order to deal with this.
Nonetheless, it can be kind of annoying to forget to reference a field with a
self. before its name. It happens a lot, and it’s annoying, frankly.
Truthiness and Operator Overloading
It’s actually quite handy and surprisingly readable that you can check for an object’s general “emptiness” just by checking its “truthiness”. But, as an overloadable operator, there are almost never any times to bother implementing
__bool__() on a custom type. Doing so for anything other to signify emptiness for a some sort of collection is likely to just be confusing for the next person to use it.
The same can be said about all operator overloading; what it does may not be completely obvious. People abusing operator overloading in C++ is what made Java decide to not include it. But it can be so nice to use operators instead of method calls. Just in case, though, you should almost always provide a well-named method that does the same thing as the operator.
I literally wrote what I’m hoping is the book on descriptors, so I have a strong opinion about them. In a very loose sense, they’re a form of operator overloading, so the same arguments about operator overloading apply here. But there’s also the fact that descriptors (usually) work differently from class and instance level, as well as the difficulties of creating a well-designed descriptor storage system. Those make it a lot harder to justify their existence. But then you realize what has and can be done with them, and you realize that it’d be difficult to live without them (especially since they’re what make methods and properties work).
I prefer the way that Kotlin did delegated properties, but the way Python’s work is pretty cool, too.
Python has first-class functions (really easy to access ones at that; you don’t need a
:: operator) and lambdas, which are the first steps toward making a language great for functional programming. Next, they have top-level functions (they don’t need to be part of a class), which helps even more. But their lambdas are limited to a single expression, which is sometimes a heavy limitation, and immutability is nearly impossible in Python.
So, as you can see, there’s good and bad to Python, as is the case for all languages. Unless a language can transcend the static vs dynamic debate, the OO vs FP debate, compiled vs interpreted debate, etc, every language will have pros and cons.