Writing readable code

Writing readable code#

We have now covered enough Python for us to talk a little bit about readability.

So far we have not really been concerned with writing “good” code, or really with judging the “quality” of our code at all. We have just been learning bits and bobs of Python and trying to write code that does what we would like it to do. Of course, what constitutes “good” code is highly subjective, one person’s “good” code is another’s “absolute disaster”, so we would probably be better off talking about more objective metrics. In programming discourse you will often hear about readable code, which is a slightly more clear-cut descriptor. If your code is readable, it should be possible for someone else to have a quick scan through it and get a rough idea of what it is supposed to do and why it is written in the way that it is.

It’s worth us pausing for a moment to consider a few reasons why we should care about making our code readable:

  • It is much easier to share code with other people if you don’t need to spend hours on end explaining what it does and how to use it.

    • This is true not only for other people, but also for your future self! It is quite incredible how quickly you can forget why you wrote your code from a few weeks ago the way that you did. Making your code as readable as possible mitigates this problem and ensures that you can pick up where you left off much more quickly.

  • Making your code more readable often makes it easier to spot bugs. Simple things like poorly named variables are often the cause of very difficult to find problems.

    • This is also important if you are asking other people for help with your code. If they can understand what you have written relatively quickly, then they are much more likely to be able to offer meaningful advice rather than having to stare at your monitor for a good half an hour.

As mentioned above, readability is definitely not objective, but nonetheless there are at least a couple of simple things you can do that most people would agree are a good idea.

Variable names#

When we first looked at variables, we saw that they can be named almost anything that you would like:

a = 5

print(a)
5
a_number = 5

print(a_number)
5
ghdioaspghidasp = 5

print(ghdioaspghidasp)
5

Hopefully we can all agree that the last example is by far the worst. In general, it is best to name your variables according to these rough guidelines:

  • Avoid single letter names unless there is a very good reason to use one.

    • One example where this makes some sense is to reproduce pen and paper notation e.g. using K to represent an equilibrium constant. Even then, it is usually better to encode some additional information such as K_298 for the equilibrium constant at \(298\,\)K.

  • Don’t be afraid, within reason, to be very explicit. For example, if we’re defining a variable in which we will store a list of elemental symbols, go with elemental_symbols rather than el_symbs or something to that effect.

    • There are obviously limits to this. If your variable name has \(> 4\) separate words in it, it’s probably annoyingly long to keep typing.

  • Avoid UPPER CASE unless there is a natural reason to use it.

    • Much like single letter names, there are some situations in which this does make sense e.g. delta_G for \(\Delta G\).

Whitespace#

As you’ve probably gathered by now, Python is relatively relaxed when it comes to whitespace. For example, we can type basic addition like this:

5+7
12

Or like this:

5 + 7
12

This same principle applies to much of Python’s basic syntax such as variable assignment:

a=5

Versus:

a = 5

Or in lists:

elemental_symbols = ['Li','Na','K','Rb','Cs','Fr']

Versus:

elemental_symbols = ['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']

The same goes for other data structures like dictionaries:

regular_shape_edges = {3:'Triangle',4:'Square',5:'Pentagon',6:'Hexagon',7:'Heptagon',8:'Octagon'}

Versus:

regular_shape_edges = {3 : 'Triangle', 4 : 'Square', 5 : 'Pentagon' , 6 : 'Hexagon' , 7 : 'Heptagon' , 8 : 'Octagon'}

In fact with data structures, we can insert even more whitespace:

regular_shape_edges = {3 : 'Triangle',
                       4 : 'Square',
                       5 : 'Pentagon',
                       6 : 'Hexagon',
                       7 : 'Heptagon',
                       8 : 'Octagon'}

You get the general idea.

It’s worth bearing in mind that sometimes you can take this concept too far. If you really wanted to, you could write Python like this:

a                       = 5
b      = 11.53
c                                                                         = -22

print         (                           a,                   b,                              c)
5 11.53 -22

But hopefully you can see that this is anything but readable.

In addition to the examples above, we can also use whitespace at a higher level to create blocks of code:

import math

numbers = [1, 2, 3, 4, 5, 6]
letters = ['a', 'b', 'c', 'd', 'e', 'f']

sqrt_2 = math.sqrt(numbers[1])
len_letters = len(letters)

print(sqrt_2)
print(len_letters)
1.4142135623730951
6

Here we have separated our code into four blocks, one for import statements, one for defining variables, one for performing some calculation with those variables and one for printing the results of that calculation. This is certainly a somewhat trivial example but the principle holds all the same: you can use whitespace to essentially create “paragraphs” in code, to organise collections of lines in a logical fashion.

Documentation#

Whilst naming your variables sensibly and laying out your code logically might help, there are many situations in which it is impossible to make your code sufficiently readable just by adjusting the code itself. This is why we use documentation: additional information about the code that is not code in itself.

We have already met one forms of documentation: comments. As a reminder, comments are messages following a # which can be placed within your code to provide additional context where necessary:

temperatures = [301.5, 298.1, 296.9, 302.3, 305.0]

# Calculate the mean temperature
mean_temperature = sum(temperatures) / len(temperatures)

This particular comment is not necessary, as the code is relatively self-explanatory, but it demonstrates the use of comments well enough in any case. Comments can also go on the same line as code:

a = 5 # Defining the value of a.

Another way to document your code is to use Markdown cells, which are not an inherent feature of Python but of Jupyter notebooks. Comments are generally best saved for small messages within your code to call out specific parts that are not immediately easy to interpret, whereas Markdown cells are better suited for longer form explanations interspersed between blocks of code (code cells). The words you are reading right now were written in a Markdown cell in a Jupyter notebook, and serve as a good example of how they can be interwoven amongst code cells to provide necessary context.

You can create a Markdown cell in a Jupyter notebook following the guidance available here (see Switching cell type). Markdown is a markup language which can be used to create nicely formatted text, equations, tables etc. For now, you can just treat Markdown cells as big block comments, don’t worry so much about fancy formatting such as bold or italics and all that jazz - we’ll come to that later.


Now that you have a rudimentary understanding of how to write readable code and how to document what it is supposed to do, try to practice these things as you move forwards. In general, the more readable your code is in the first place, the less documentation it will likely require. Most of this is habitual and can be roughly equated to trying to improve your handwriting, just in programming terms instead. If you get used to following (roughly) the guidelines presented here, then soon enough you won’t even have to think about it any more and your future self will be very thankful indeed!