June 5, 2020

Python warts

I have in depth experience with a number of other languages: C, ruby, perl, javascript, and Haskell to name a few. So when I look at Python, it is not from the eyes of a first time programmer who simply thinks, "this is how things must be". I know things can be different, and recognize when things are just weird. Different is fine. Weird is, well ... just weird.

Indentation is syntax

I wasn't even going to mention this. All kinds of venom has been expended on this, and I don't really want to add to it. It was the thing that turned me off to the language for several years. However, I am helping a beginner learn python and twice now they have sent me code they can't figure out and the issue is something indented that shouldn't be. All this rubbish about indenting being user friendly and avoiding the issues other languages have with braces is just wrong. The indentation things simply introduces another class of mistakes that are at least as common, especially for the beginner.

Just make sure that your editor handles white space in a consistent way. I made my peace with this working with Haskell. The vital thing was adjusting vim settings so that it never placed tabs in a file. There is nothing worse than some invisible white space issue going on where the compiler has one idea and you cannot see it.

The range function

Ruby has the principle of least surprise, which is a superb rule to guide the design of a language. On the other end of the spectrum we have something like the Python range function. The absurd thing is the end of the range -- you have to specify one greater than where you want it to stop! So if you ask for range(1,10), you get 1 through 9! Makes sense?? And the second argument is not the count of objects, which might sort of make sense if you wanted to loop over 10 items using 0 through 9. If you ask for range (5,7), you get 5 and 6. What were these people smoking that day?

The global statement

There is nothing surprising about a global statement or the notion of global scope. Python could have handled it in many ways. Functions could have access to global scope by default or functions would always require a global declaration to make things clear. The weird thing in Python is that functions have access to global scope by default, but only for read access. The global declaration is required if a function wants to modify a variable. This is just weird and inconsistent.

Tuples

The question is why have tuples in addition to lists. The standard answer is that tuples are immutable. Ultimately, this is just pointless double talk. So what? No other language I know of has anything like this distinction and everything is fine, the world goes on.

Too many irrelevant objects

A lot of functions in Python return some specialized object when they should just return a list. A good example is "filter", which we give a list to and ask it to select just those things from it that match some condition. An intelligent person would expect filter to return a list. But it doesn't, and you have to write code like this, which is stupid:
good_stuff = list(filter(lambda xx: xx != '', stuff))
Now apparently this filter object is iterable, and could be used as if it was a list in a for loop. Don't try to call len() on it though. It might just be better to act like filter doesn't exist and use a list comprehension.

List oriented looping

I am not sure whether to call this a virtue or a liability. Python does not provide a "for" loop like C and Javascript programmers are used to! Well, ruby doesn't either, and maybe there is something to be said for letting lists drive loops by themselves and not having index variables to drive every loop. You do have a while loop and can construct anything you need by hand using it.

Function or method?

Python is entirely capricious about whether a part of the language is a method or a function that stands on its own. Consider "sum" and "len" applied to lists. I would expect to write xyz.len (or xyz.size), but you write len(xyz). The same for "sum", you don't write xyz.sum, but you write sum(xyz). And the same for close, do you write close(file) or file.close()? A clean design would make all of these objects. At the least, it would be nice if things were more consistent. No doubt there is some history here. Too bad python 3 didn't clean things up.
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org