Yes, the title of this blog is a three-way pun, although neither Lisp nor conferences will be mentioned.
There was a nice article on Quora recently replying to a question on the downsides of the Python programming language
While a lot of the answers are good, there are a couple of nits to pick.
The Role of Objects
For example, the claim that Python is slow because it is interpreted and dynamically typed is not accurate. Python is slow because everything is an object.
For comparison, Perl is both dynamically typed and interpreted but can be very fast for numerical programming because at the end of the day $foo refers to an IEEE 754 floating point number occupying four or eight bytes of memory, and nothing else. That means the interpreter gets to compile $foo*$bar down to a single MUL instruction, or something very much like it. It’s still inside the interpreter, but the interpreter gets to be very close to the metal in an important class of special cases like this.
In Python, “foo” is a reference to a Python object that has a floating point number hiding somewhere deep inside it. That means that in contrast to the Perl case, in Python there are likely dozens of instructions required to pull the values out of their respective objects, multiply them, and build an object to hide the result in. Ergo: slow.
I ran into this phenomenon doing some numerical programming in Perl, which I did because CPAN happened to have a very simple 4th order Runge-Kutta solver. I was solving a nasty system of OEDs whose solutions had cusps scattered along the time axis, and it was easy to add some adaptive logic to adjust the time step appropriately. It still ran more slowly than I liked, so I translated it into C++ and was surprised to when it didn’t even run twice as fast. It turns out dynamically typed, interpreted languages can be pretty fast after all.
Python can get the same performance by using Cython to provide type information, which breaks the “everything is an object” model.
Consistent Container Interface Via Built-in len()
The second point I want to talk about is the “len()” built-in function. This is actually a positive and a very clever piece of language design. It ensures consistency across different container types, so we don’t end up with x.len(), y.Length(), z.size(), etc. There really is no other way of enforcing this as a language idiom. Even if there had been a “Container” class that all container-like objects had to derive from, people would have ignored it (citing “overhead” or similar) and we would have a large number of slightly different interfaces to find the length of something.
Furthermore, not all things with lengths are containers. Strings are not, for example, insofar as they can only contain characters, not objects.
Choosing the “len()” built-in as the standard interface to get the length of things strongly supports the Python philosophy of “There should be one–and preferably only one–obvious way to do it.”
Python is used by large companies at an enterprise level which shows that despite some issues it is a powerful language. It’s particularly impressive that Python is also popular as a teaching language, which shows that it scales to cover both small and large, novice and expert, use cases.
Any successful language likely includes some significant tradeoffs in its design, and understanding them can help developers work with the language instead of against it. Hopefully this article has helped provide a bit more detail to clear up some the tradeoffs.
Title image courtesy of Yuko Honda on Flickr under Creative Commons License.