This document defines the coding style to use with Python and Matlab at the Mobility and Adaptive Sports Research Lab.

This guide follows these conventions:

Although these conventions were designed for Python, we try to be consistent across both Python and Matlab environments, so we also follow PEP8 and Numpy docstrings in Matlab.

The most important rules are described in this document. You cannot be completely wrong if you follow every rule defined in this document and, for Python coders, if you use the tools mentioned in section Tools for Python coders.

Why bothering with a coding style?

Adopting a coding style is recognizing this fact: code is more often read than written, and people who read the code are often not the ones who wrote it. By respecting a coding style, the code is clearer, more homogeneous, and easier to understand and reuse.

Language

Use English. Most programming languages use English keywords; for coherence, it just makes sense to program in English, including comments. If the code is used explicitly for formation in another language (here it would be French), it is okay to write comments in French. Otherwise, since sharing code is a great way to collaborate, and eventual collaborators may not speak French at all, then code in English.

To be consistent with this convention, this guide is also written in English to avoid the needs for constant translations (e.g., integer/entier, string/chaîne de caractères, etc.).

Line width

Limit the line width to 79 characters.

Variables

Explicit names

The names of variables should document their meaning or use and avoid abbreviations or symbols. This works:

v = p / t

but this will be clearer for the reader:

velocity = position / time

Variable names are in lower case, with words separated by underscores:

position
mean_velocity
n_participants

Passive form

Variables should be passive, using nouns (with or without qualifiers):

calculated_forces  # good (passive)
calculate_forces   # bad (active form)

Temporary variables

For temporary variables with a small scope (no more than a few lines), it is correct to use very short names, even one letter long. For example, it is common practise to use i and j as temporary indexes, xyzt as temporary space and time coordinates, and s as temporary strings. Still, it is generally a good idea to use meaningful names instead of letters.

# python

for i in range(10):  # ok to use i
    process_recording(i)
% matlab

for i = 1:10  % ok to use i
    process_recording(i);
end

Prefixes i and n

Use the prefix i for indicating an index, and n for indicating a total count:

# python

for i_recording in range(n_recordings):
    process_recording(i_recording)
% matlab

for i_recording = 1:n_recordings
    process_recording(i_recording);
end

Plurals

Use plural to denote a list (array) of elements, and singular to denote a single element.

# python

participants = ['P01', 'P02', 'P03']

for participant in participants:
    process_participant(participant)
% matlab

participants = {'P01', 'P02', 'P03'};
for participant = participants
    process_participant(participant);
end

Negated boolean names

Avoid using negated boolean variable names, such as is_invalid, because it creates confusion when used in combination with logic operators. For example, ~is_invalid is a double negation that means valid, which is confusing.

is_not_found  # bad
is_found      # good

is_invalid    # bad
is_valid      # good

Reserved names

Avoid using reserved names (names that are part of the language), since it causes clashes that are difficult to debug. A better way is to suffix the variable name with an underscore, and the best way is to find a synonym.

set = [1, 2, 3]       # bad (clash with the set command)
set_ = [1, 2, 3]      # better
the_set = [1, 2, 3]   # best

Constants

Neither Python or Matlab do have true constants. It is therefore a good idea to adopt a practice that makes constants explicit so that someone does not unintentionally redefines its value. Use capital letters separated with underscores:

SERIAL_NUMBER = '123ABC'
WHEEL_RADIUS = 0.3

Dictionaries (Python) and structures (Matlab)

The key names of a dictionary (Python), or field names of a structure (Matlab) are passive, in mixed case starting with a capital.

# python

anthropometrics = {}
anthropometrics['Weight'] = 75  # kg
anthropometrics['Height'] = 1.70  # m
anthropometrics['DateOfBirth'] = '2000-01-01'
% matlab

anthropometrics = struct()
anthropometrics.Weight = 75  % kg
anthropometrics.Height = 1.70  % m
anthropometrics.DateOfBirth = '2000-01-01'

Avoid repeating the dictionary/struct name in the key/field names.

# python

segment['SegmentLength'] = 0.40  # Bad
segment['Length'] = 0.40  # Good
% matlab

segment.SegmentLength = 0.40  # Bad
segment.Length = 0.40  # Good

Units

In general, use the International System of Units (SI) for values. When SI Units could not or should not be used, consider adding the unit as a suffix.

angle          # good, we assume radians
angle_radians  # good but not required
angle_degrees  # good
angle          # bad if the unit is degrees

Functions

Explicit names

As for variables, the names of functions should document their use. Avoid using abbreviations.

Function names are in lower case, separated by underscores:

plot()
calculate_local_coordinates()

Active form

Since a function performs an action, it should start with a verb.

calculate_forces()   # good (active)
calculated_forces()  # bad (passive)

Docstrings

Every function should have a docstring section to indicate what is the purpose of the function, and how to use it. Use the numpydoc format.

Python, without using type annotations

def calculate_norm(x, y, z):
    """
    Calculate the norm of a vector.

    Here we would put an extended, multi-line description of the function, if needed. 

    Parameters
    ----------
    x, y, z: float. The components of the vector.

    Returns
    -------
    float

    """
    return np.sqrt(x ** 2 + y ** 2 + z ** 2)

Python, using type annotations

def calculate_norm(x: float, y: float, z: float) -> float:
    """
    Calculate the norm of a vector.

    Here we would put an extended, multi-line description of the function, if needed. 

    Parameters
    ----------
    x, y, z: The components of the vector.

    Returns
    -------
    float

    """
    return np.sqrt(x ** 2 + y ** 2 + z ** 2)

Matlab

function norm = calculate_norm(x, y, z)
    % Calculate the norm of a vector.
    %
    % Here we would put an extended, multi-line description of the function, if needed. 
    %
    % Parameters
    % ----------
    % x, y, z: float. The components of the vector.
    %
    % Returns
    % -------
    % norm: float. The norm of the vector.

    norm = sqrt(x.^2 + y.^2 + z.^2);
    return
end

Symmetry

Two functions that have opposite actions should have matched names:

  • add/remove
  • start/stop
  • begin/end
  • insert/delete
  • increment/decrement
  • open/close
  • show/hide
  • suspend/resume
  • etc.

Classes

Passive form

As for variables, classes are passive, using nouns. Class definitions use mixed case starting with a capital letter.

# python

TimeSeries                 # This is the name of the class
timeseries = TimeSeries()  # This is an instance of the TimeSeries class.

Tools for Python coders

For those who are coding in Python in the Spyder environment, you can do the following to ensure that your code complies to other PEP8 specifications that are not covered in this guide, such as the maximal number of blank lines allowed between sections, the number of characters per line, etc.

  • In Preferences : Completion in linting : Code style and formatting : Code style, check Enable code style linting to enable PEP8 linting;
  • In Preferences : Completion in linting : Code style and formatting : Code formatting, Select autopep8 and check Autoformat files on save to ensure minimal PEP8 compliance at all times;
  • In Preferences : Completion in linting : Docstring style, check Enable Docstring style linting and select Numpy to enable Numpy docstring linting.