XClose
Menu

Coding Conventions

Let's import first the context for this chapter.

In [1]:
from context import *

One code, many layouts:

Consider the following fragment of python:

In [2]:
import species
def AddToReaction(name, reaction):
    reaction.append(species.Species(name))

this could also have been written:

In [3]:
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

In [4]:
reaction = {
    "reactants": ["H", "H", "O"],
    "products": ["H2O"]
}
In [5]:
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. This convention is used broadly in the python community.

In [6]:
class ClassName:
    def methodName(variable_name):
        instance_variable = variable_name

This other example uses underscore_separation for all the names.

In [7]:
class class_name:
    def method_name(a_variable):
        m_instance_variable = a_variable

Hungarian Notation

Prefix denotes type:

In [8]:
fNumber = float(sEntry) + iOffset

So in the example above we know that we are creating a float number as a composition of a string entry and an integer offset.

People may find this useful in languages like Python where the type is intrisic in the variable.

In [9]:
number = float(entry) + offset

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.

In [10]:
anothervariable += 1
if ((variable == anothervariable) and flag1 or flag2): do_something()
In [11]:
anothervariable = anothervariable + 1
variable_equality = (variable == anothervariable)
if ((variable_equality and flag1) or flag2):
    do_something()

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.

Syntax choices

  • Explicit operator precedence
  • Compound expressions
  • Package import choices

Coding Conventions

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

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

Lint

There are automated tools which enforce coding conventions and check for common mistakes.

These are called linters:

E.g. pip install pycodestyle

In [12]:
%%bash
pycodestyle species.py
species.py:2:6: E111 indentation is not a multiple of four
---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
<ipython-input-12-24d47916ce0c> in <module>
----> 1 get_ipython().run_cell_magic('bash', '', 'pycodestyle species.py\n')

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
   2321             magic_arg_s = self.var_expand(line, stack_depth)
   2322             with self.builtin_trap:
-> 2323                 result = fn(magic_arg_s, cell)
   2324             return result
   2325 

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magics/script.py in named_script_magic(line, cell)
    140             else:
    141                 line = script
--> 142             return self.shebang(line, cell)
    143 
    144         # write a basic docstring:

<decorator-gen-109> in shebang(self, line, cell)

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magics/script.py in shebang(self, line, cell)
    243             sys.stderr.flush()
    244         if args.raise_error and p.returncode!=0:
--> 245             raise CalledProcessError(p.returncode, cell, output=out, stderr=err)
    246 
    247     def _run_script(self, p, cell, to_close):

CalledProcessError: Command 'b'pycodestyle species.py\n'' returned non-zero exit status 1.

It is a good idea to run a linter before every commit, or include it in your CI tests.

There are other tools that help with linting that are worth mentioning. With pylint you can also get other useful information about the quality of your code:

pip install pylint

In [13]:
%%bash
pylint species.py
************* Module species
species.py:2:0: W0311: Bad indentation. Found 5 spaces, expected 4 (bad-indentation)
species.py:1:0: C0111: Missing module docstring (missing-docstring)
species.py:1:0: C0111: Missing class docstring (missing-docstring)
species.py:1:0: R0205: Class 'Species' inherits from object, can be safely removed from bases in python3 (useless-object-inheritance)
species.py:1:0: R0903: Too few public methods (0/2) (too-few-public-methods)

-------------------------------------
Your code has been rated at -15.00/10

---------------------------------------------------------------------------
CalledProcessError                        Traceback (most recent call last)
<ipython-input-13-10eb1f611741> in <module>
----> 1 get_ipython().run_cell_magic('bash', '', 'pylint species.py\n')

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/interactiveshell.py in run_cell_magic(self, magic_name, line, cell)
   2321             magic_arg_s = self.var_expand(line, stack_depth)
   2322             with self.builtin_trap:
-> 2323                 result = fn(magic_arg_s, cell)
   2324             return result
   2325 

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magics/script.py in named_script_magic(line, cell)
    140             else:
    141                 line = script
--> 142             return self.shebang(line, cell)
    143 
    144         # write a basic docstring:

<decorator-gen-109> in shebang(self, line, cell)

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magic.py in <lambda>(f, *a, **k)
    185     # but it's overkill for just that one bit of state.
    186     def magic_deco(arg):
--> 187         call = lambda f, *a, **k: f(*a, **k)
    188 
    189         if callable(arg):

~/virtualenv/python3.6.3/lib/python3.6/site-packages/IPython/core/magics/script.py in shebang(self, line, cell)
    243             sys.stderr.flush()
    244         if args.raise_error and p.returncode!=0:
--> 245             raise CalledProcessError(p.returncode, cell, output=out, stderr=err)
    246 
    247     def _run_script(self, p, cell, to_close):

CalledProcessError: Command 'b'pylint species.py\n'' returned non-zero exit status 28.

and with black you can fix all the errors at once.

black species.py

These linters can be configured to choose which points to flag and which to ignore.

Do not blindly believe all these automated tools! Style guides are guides not rules.

Finally, there are tools like editorconfig to help sharing the conventions used within a project, where each contributor uses different IDEs and tools. There are also bots like pep8speaks that comments on contributors' pull requests suggesting what to change to follow the conventions for the project.