Don’t Teach With Python

Garth Gilmour
6 min readNov 20, 2021

--

Scene from Goodbye Mr Chips

Introduction.

Here’s a bitter truth for educators. Inside every good teacher is a bad teacher trying to get out. As time goes by we become hardened and embittered . We drift away from contemporary concerns. Our war stories lose relevance and therefore value. Increasingly we become caricatures of our former selves.

The ultimate danger is that one day we wake up and find it’s just not our world anymore. This is a very real issue for teachers, but I believe it applies equally well to the programming languages we use in our teaching.

Recently I’ve come back to Python, after a lengthy absence. I still think it’s a great language, well suited for a huge variety of tasks. At Instil we know of many teams, across a variety of industries, who are succeeding with Python.

What I take issue with is the idea that Python is a simple language, and hence an ideal first language to be used in secondary and higher education. On the contrary, I think time has taken its toll and the mantle of ‘best teaching language’ should be passed on to something else.

In support of my view here are four reasons not to pick Python as a language for teaching beginners. Before we begin please note I fully accept that:

  • Python remains a great choice for professional developers.
  • The Python designers are far smarter than I can ever hope to be.
  • All the decisions that led to the features mentioned below were made for good reasons and with only the best of intentions.

However, from the perspective of the schoolroom, all accidental complexity is unforgivable. It doesn’t matter how or why the language acquired it’s blemishes. What counts is how much they impede learning.

1. There’s more than one way to do it.

My first problem with Python is the variety of options for accomplishing common tasks. Let’s say you want to print out a string with values inserted into it. We have at least three options:

As of Python 3.6 the final option is best. But you will find lots of examples of the others. Plus of course calls to print that pass in multiple values and/or use of the concatenation operator.

Now let’s say we have a function that returns a sequence, and we want to unpack / de-structure the contents. Again we have three options:

Additionally we can rewrite the build_data function without the parenthesis on the return value. Because multiple items are automatically wrapped in a Tuple:

This equivalence and optionality of punctuation may be convenient to experienced developers. But it confuses the hell out of beginners.

Moving on let’s say I want to I want to filter a list. This time we have five choices:

Experienced Pythonistas will want to say that data3 and data4 are the same thing. Which is precisely what I’m getting at. When working with collections the Python coder has a mental flowchart of options that includes slicing, ranges, sequence functions, methods, functional operators, list comprehensions and generators. These in turn have their own variations and defaults. Building (and refreshing) this chart is a laborious process that we might not wish to inflict on future generations.

Once again I’m not arguing that Python is a bad language, either in isolation or by comparison to any other. But the idea that Python is a simple language, with a single canonical way of accomplishing any given task, is pure fiction.

Against this you could argue that one should simply use the latest version of each feature. But by that standard any programming language is elegant and straightforward. Except possibly C++.

2. My God, it’s full of stars!

My second problem with Python is that it’s a verbose language, except when it doesn’t want to be. Take for example the many uses of the star. We use a star to indicate a varargs parameter, as below:

But the star is also used to indicate that parameters must be passed by name. So if a beginner accidentally types a comma then the meaning changes completely:

Of course the star is also used to flatten inputs and when unpacking. Plus two stars denotes a table to be populated with any unknown parameters which were passed by name.

So we can have all kinds of fun:

This produces the output below, which is far from obvious:

123
[‘abc’, ‘def’, 456, ‘foo’]
bar

If there was a varargs or params keyword a lot of this could be avoided. Or even distinct symbols like the ellipsis. That’s what I would expect in a language aimed at beginners.

3. This isn’t the scope you’re looking for

Problem area number three is related to scopes. The first time a junior programmer writes something like this it inevitably causes confusion:

Even folks who haven’t used compiled languages expect the variable created within a for to be scoped to the loop. Which it is not. But that’s just a warm up for something like this:

Here trying to read my_global in a function does not create a new local variable, but writing to it does (as in demo2). We can use either the global keyword or theglobals function to access the global variable, but it’s hardly intuitive. All these complexities are before we consider closures and the nonlocal keyword.

The root cause of these issues is the concept that variables should be created without a keyword. To my mind this is a terrible idea, because it opens the door to shadowing issues and ‘declaration by typo’. Explicit beats implicit after all…

4. Object or Functional? Typed or Untyped?

My final, and most serious problem with Python is that it seems undecided on what kind of language it wants to be. As regards data typing consider the following:

Here myvar1 is a reference to the object for the bool type, whereas myvar2 is an uninitialised variable with a type hint of boolean. Neither does what the student intended, which was to store a boolean in a variable.

Even if there is some debate about how strongly typed Python wants to be, you might imagine the position on Functional Programming is clear. Historically Python was never intended to be an FP language, as can be clearly seen in the rules for lambdas:

  • We have to use the lambda keyword when declaring lambdas
  • A lambda is restricted to only containing a single expression
  • The style guide discourages references to lambdas. Instead they are intended to be used as parameters to methods like filter and map

This is fine by me. I’m happy for a language to be opinionated, not least because it makes my job easier. But consider the following:

In Python the name of the function is equivalent to its address. Which is a far more pervasive source of errors for beginners than issues with lambda declarations. Yet I am forced to write lambda a: a + 1 but not ::sample or suchlike. As an educator this seems to be the wrong emphasis.

Additionally core language features like decorators are implemented via higher order functions. Here’s an example which verifies that the annotated function has been invoked with integers:

So is Python a functional language or not? The picture isn’t entirely clear…

Turning to OO we again have flexibility in all the wrong places. I can declare a class like this:

Which is grand. But I can also declare it like this:

Because there is no requirement that the self parameter be given that name. Of course I should be declaring the fields with a double underscore for privacy:

Which means that the names will be mangled as _Class__field. But the beginner is still allowed to type object.__field which creates a new field on the instance. The fact that the same symbol can be used both inside and outside the object, but refers to two separate attributes, is perplexing to say the least.

Magnifying all of this is the confusion mentioned earlier regarding sequences and built in functions. In other languages there are collection literals, but collections themselves are always manipulated via methods. We always say things.sum() and never sum(things). But in Python some operations are free functions on sequences whereas others are methods. Similarly filter and map are built in, but reduce is banished to a module and other FP operations seem to be verboten. When teaching it’s hard to draw boundaries around this, and thereby give students the clarify they need.

Conclusions

To my mind Python is no more or less a mess than any general purpose language of it’s vintage. In fact you could make the case that its contemporaries have aged far less well. After all Java and C# can certainly be considered as triumphs when it comes to preserving backwards complexity, but for a newbie they are a hot mess of overlapping and conflicting functionality.

However none of these comparisons matter to educators. Our concern should be for the welfare of our students. It doesn’t matter what obstacles a language has overcome or how well it carries its scars. The only thing that matters is how well it facilities learning. From that perspective there are many better alternatives to Python available. Pick one.

--

--

Garth Gilmour

Helping devs develop software. Coding for 30 years, teaching for 20. Google Developer Expert. Developer Advocate at JetBrains. Also martial arts and philosophy.