Coding Style Guide

The main principles behind pulse2percept development are:

  • Robustness: The results of a piece of code must be verified systematically, enhancing stability and reducing redundancies.
  • Readability: The code is read much more frequently than it is written, and should be easy to understand by other developers. Documentation is essential.
  • Consistency: Following these guidelines will ease reading the code and will make it less error-prone.

Coding style

pulse2percept uses the standard Python PEP8 style guide to ensure readibility and consistency of the code base.

Note

There are tools that allow you to automatically check your Python code against PEP8 style conventions.

For example, if you work with Sublime, you can use the PEP8 Autoformat package. For convenience, make sure the setting autoformat_on_save is set to true.

A few general guidelines:

  • Use 4 spaces instead of tab.
  • Limit all lines to a maximum of 79 characters.
  • Write code for Python 3.5+.

Imports

pulse2percept recommends using the following package shorthands to increase consistency and readibility across the library:

import numpy as np
import numpy.testing as npt
import scipy as sp
import pandas as pd

import pulse2percept as p2p

Whitespace and blank lines

Don’t use spaces around the = sign to indicate a keyword argument:

# Good:
def complex(real, image=0.0):
    return magic(r=real, i=imag)

# Bad:
def complex(real, image = 0.0):
    return magic(r = real, i = imag)

Surround top-level function and class definitions with two blank lines. Use blank lines in functions, sparingly, to indicate logical sections.

Breaking long lines (W503 vs W504)

The recommended style is to break a line after binary operators:

# Good:
income = (gross_wages +
          taxable_interest -
          student_loan_interest)

# Bad:
income = (gross_wages
          + taxable_interest
          - student_loan_interest)

Yes, we are aware of the ongoing W503 vs W504 discussion. W504 would improve readibility. However, for now, we want to ensure consistency with the existing code.

Class layout (slots, properties)

Classes can have __slots__ that list all class attributes. This can have several advantages:

  • Users cannot add new class attributes on-the-fly. This is desirable for models (e.g., AxonMapModel), which has a predefined number of attributes.
  • Slots reduce memory usage. This is desirable for stimuli (e.g., TimeSeries), for which a large number of instances are created.

Subclasses that inherit from classes with __slots__ must themselves implement slots. The top-level class must inherit from object, and attributes cannot be listed more than once:

class Vehicle(object):

    __slots__ = ('owner')

    def __init__(self, owner):
        self.owner = owner


class Car(Vehicle):

    __slots__ = ('n_doors')

    def __init__(self, owner, n_doors):
        self.owner = owner
        self.n_doors = n_doors

If you did it right, then neither Vehicle nor Car should have a __dict__ attribute:

    car = Car('myself', 4)
    assert hasattr(car, '__slots__')
assert not hasattr(car, '__dict__')

This guide was inspired by the `DIPY style guide <https://dipy.org/documentation/1.1.1./devel/coding_style_guideline>`_.