7.2 Coding conventions#

Estimated time for this notebook: 10 minutes

One code, many layouts:#

Consider the following fragment of python:

import species


def AddToReaction(name, reaction):
    reaction.append(species.Species(name))

this could also have been written:

from species import Species


def add_to_reaction(a_name, a_reaction):
    l_species = Species(a_name)
    a_reaction.append(l_species)

So many choices#

  • Layout

  • Naming

  • Syntax choices

Layout#

reaction = {
    "reactants": ["H", "H", "O"], 
    "products": ["H2O"]
}
reaction2 = {
    "reactants": 
        [
            "H", 
            "H",
            "O"
        ], 
    "products": [
        "H2O"
    ]
}

Layout choices#

  • Brace style

  • Line length

  • Indentation

  • Whitespace/Tabs

Inconsistency will produce a mess in your code! Some choices will make your code harder to read, whereas others may affect the code. For example, if you copy/paste code with tabs in a place that’s using spaces, they may appear OK in your screen but it will fail when running it.

Naming Conventions#

Camel case is used in the following example, where class name is in UpperCamel, functions in lowerCamel and underscore_separation for variables names:

class ClassName:
    def methodName(self, variable_name):
        self.instance_variable = variable_name

This example uses underscore_separation for all the names:

class class_name:
    def method_name(self, a_variable):
        self.m_instance_variable = a_variable

The usual Python convention (see PEP8) is UpperCamel for class names, and underscore_separation for function and variable names:

class ClassName:
    def method_name(self, variable_name):
        self.instance_variable = variable_name

However, particular projects may have their own conventions (and you will even find Python standard libraries that don’t follow these conventions).

Newlines#

  • Newlines make code easier to read

  • Newlines make less code fit on a screen

Use newlines to describe your code’s rhythm.

Syntax Choices#

The following two snippets do the same, but the second is separated into more steps, making it more readable.

big = True
fast = False
color = "brown"
cheap = True

if color == "red" and fast or big and cheap:
    print("Vrroom!")
Vrroom!
exciting = color == "red" and fast
practical = big and cheap

if exciting or practical:
    print("Vrroom!")
Vrroom!

We create extra variables as an intermediate step. Don’t worry about the performance now, the compiler will do the right thing.

What about operator precedence? Being explicit helps to remind yourself what you are doing.

  • Explicit operator precedence

  • Compound expressions

  • Package import choices

Type Annotations#

Python is dynamically typed, which means if a variable x is an integer:

x = 32

it is valid in Python to make it into a string or any other type later:

x = "bananas"

This is not the case in a statically typed language, like C++ or Java. Having this flexibility in Python can be convenient but it can also lead to unexpected, and potentially difficult to diagnose, mistakes if variables in your code have different types to what was expected.

For example, consider the following function:

def repeat(x, y, times=2):
    return (x + y) * times


repeat("dog", "woof")
'dogwoofdogwoof'

It looks like a function that repeats its inputs a number of times, but what if the inputs are numbers?

repeat(2, 3, times=3)
15

Ah, that’s not what we wanted (we were hoping for 232323).

To help us remember how the function is supposed to be used, we can add type annotations (or type “hints”):

def repeat(x: str, y: str, times: int = 3) -> str:
    return (x + y) * times

The syntax variable_name: type indicates the type each parameter should have (x and y are strings, and times is an integer), and the arrow syntax in function_name(...) -> type indicates the type of data the function returns (a string for the repeat function above).

Note that type annotating your code will not change it’s behaviour (Python does not enforce variables to be their annotated types):

repeat(2, 3, times=3)
15

But they form a kind of documentation to help us understand how the function should be used, and there are tools that can use them to diagnose issues in your code (see the “Linters” section).

In this case we could do this to get what we expected originally:

int(repeat("2", "3", times=3))
232323

See the Python documentation for more details on type annotations and the typing library.

Coding Conventions#

You should try to have an agreed policy for your team for these matters.

If your language or project has a standard policy, use that. For example: