The Python community is never too pleased, for some reason, when people ask for extra strong-typing features to be added to the language. The gripe seems to be that strong typing is for languages that are more static than Python, and that strong typing therefore is a betrayal of everything that the language stands for.
Be that as it may, strong typing is there to prevent you from doing something stupid. The dream, I take it, is that if your program makes it through compilation, it will run properly. In that sense, the dream of strong typing is that all problems of properly running code can be reduced to problems of syntax. This is a fine thing to desire from a language.
Just now I was reminded of why it’s sad that Python doesn’t think this way. What I wanted to do was something like this:
some_list = ['a', 'a', 'b','c', 'c', 'c', 'd', 'e'] some_dict = dict() for item in some_list: if item in some_dict: some_dict[item] += 1 else: some_dict[item] = 1 for (item, count) in sorted(some_dict.iteritems(), lambda x,y: y - x): print "%s:t%s" % (count, item)
It’s just supposed to take a list of items and display them in descending order of how frequently they appear. In this code, ‘a’ appears twice, ‘b’, ‘d’, and ‘e’ once, and ‘c’ three times, so the output should be
3: c 2: a 1: b 1: e 1: d
Instead what I was getting was an error:
(10:50) slaniel@slaniel-laptop:~/python_test$ python ./dict_destruction_test.py Traceback (most recent call last): File "./dict_destruction_test.py", line 5, in <module> if item in some_dict: TypeError: argument of type 'int' is not iterable
I couldn’t figure out why it was telling me this. Turns out that instead of writing
if item in some_dict: some_dict[item] += 1 else: some_dict[item] = 1
I had written
if item in some_dict: some_dict[item] += 1 else: some_dict = 1
which wiped out
some_dict and turned it into the integer 1.
A strongly-typed language would declare that dicts are dicts and ints are ints, and never the twain shall meet. It wouldn’t allow me to squash the whole dictionary. Regardless of your ideological priors for programming languages (and it is, by the way, remarkable to me that people do often have such priors), this seems like a desirable outcome. Unless I’m missing something, Python won’t let you protect yourself in this way.
Those four lines of Python could be reduced to this one line:
some_dict[item] = some_dict.get(item, 0) + 1
Inasmuch as reducing the amount of code reduces its expected bug count, this shortening may reduce errors. But it doesn’t solve the larger problem.
What I really want, what I really really want, is to specify argument types in Python signatures — e.g.,
def some_func( int my_int, str my_str ): pass
If I’m writing library code that others are going to use, I end up writing my own garish, hackish type-checking into the function:
def some_func( my_int, my_str ): if not isinstance(my_int, int): raise ValueError("my_int must be of type 'int'; got type '%s' instead" % type(my_int)) if not isinstance(my_str, str): raise ValueError("my_str must be of type 'str'; got type '%s' instead" % type(my_str))
This could be abstracted a bit, and could be made more concise with Python decorators, but it’s still not what I want — namely, compiler-level checking that breaks as early as possible if
my_int isn’t an int and
my_str isn’t a str. Again, I want type violations to be syntax errors, not runtime errors.
The usual response in the Python community is that they believe in using unit tests rather than type-checking. I don’t understand why one has to choose.
I also don’t understand the harm in making argument-type specification optional. Right now, function prototypes look like
def func_name( arg1, arg2, ..., argN )
Now they’d just look like
def func_name( arg1_type arg1, arg2_type arg2, ... argN_type argN )
Those look completely different, so far as the compiler is concerned; looking at a bit of source code, it could tell whether the types were specified or not, and could decide whether or not to complain on the basis of a compiler directive — something like
at the top of the file. It seems to me that we can have our type checking and eat our backwards compatibility, too.