Python Style Guidelines

Style should follow PEP8 and PEP257 in cases where these guidelines do not state a different preference.

Line length.

Line length should be at most 71 visible characters, so that it is 72 with newlines. This ensures that all lines obey PEP8 and that all lines are the same length.

Numbers.

Numbers should always use separators.

number1 = 1_000
number2 = 100_000
number3 = 1_000_000

Lists.

list, set, dict objects should be defined on a single line, if they fit.

some_list = [1, 34, 423, 12, 4, 10]
some_set = {1, 45, 233, 549}
some_dict = {'a': 10, 'b': 20, 'c': 10_000}

They should be defined on a line by line basis otherwise, with opening and closing brackets on their own line. A comma should be used after the last element too.

some_list = [
    some_very_long_list_element,
    some_other_very_long_list_element,
    some_even_very_very_long_list_element,
    and_so_on,
]

some_set = {
    some_very_long_set_element,
    some_other_very_long_set_element,
    some_even_very_very_long_set_element,
    and_so_on,
}

some_dict = {
    'some_long_key': some_very_long_set_element,
    'another_long_key': some_other_very_long_set_element,
    'key': some_even_very_very_long_set_element,
    'also_a_key': and_so_on,
}

Comprehensions.

Comprehensions should be done on a single line, if they fit.

list1 = [2*x for x in range(20)]
list2 = [2*x for x in range(20) if 2*x % 4 == 0]
set1 = {2*x for x in range(20)}
dict1 = {key: value for key, value in zip(range(10), (10, 20))}

If the the comprehension does not fit on a single line, try placing the opening and closing brackets on separate lines.

some_very_long_variable_name = [
    some_element for some_element in some_long_container_name
]

some_other_very_long_variable_name = {
    key: value for key, value in zip(range(10), range(10, 20))
}

If the comprehension still does not fit, split it so that each Python keyword begins on a new line, with the exception of for and in which should be placed on the same line, if they fit.

some_very_long_variable_name = [
    some_very_long_function_name(some_very_long_element_name)
    for some_very_long_element_name in some_long_container_name
    if some_very_long_element_name == 20
    and some_very_long_element_name % 2 == 1
]

some_other_very_long_variable_name = {
    key_name: value_name
    for key_name, value_name in zip(range(10), range(10, 20))
}

Function calls.

Function calls, with 3 or fewer parameters, may be done on a single line without any parameter names.

some_variable = some_function(1, 2, 3)

They can also be done with parameter names, if they fit.

some_variable = some_function(param1=12, param2=32, param3=21)

Functions with more than 3 parameters must be called with each parameter specified by name and on a separate line.

some_variable = some_function(
    param_name1=1,
    param_name2=2,
    param_name3=3,
    param_name4='4th',
)

This keeps everything readable and ensures that parameters are not sensitive to order.

Function and method definitions.

Functions and methods should be defined so that they are on a single line, if they fit.

def some_function(param1, param2, param3, keyword_param=10):
    return 12

If the function or method does not fit on a single line it should be split so that each parameter is on a separate line. The closing ): is at the same indentation as the def keyword.

def some_function(
    some_very_long_parameter_name1,
    some_very_long_parameter_name2,
    some_keyword_parameter=12,
):
    return 12

Strings.

Strings should use , unless needs to be a character in the string.

some_string = 'abcdefg'

Multi-line strings.

Opening and closing brackets should be on separate lines and spaces should be at the end of the line, not the start.

some_multiline_string = (
    'this is a '
    'multiline '
    'string.'
)

Docstrings.

Docstrings should open and close with three double quotes “”“. The first line of a docstring should always be on the line below the opening “”“. The closing “”“ always needs to be on its own line and preceded by an empty line. The docstring must be followed by an empty line.

def some_fn():
    """
    Do something.

    """

    foo()
    return bar(12)

Docstring markup.

Docstrings should use numpy style formatting. An explanation of the markup is provided here, and this is considered required reading.

The examples below also explain how the markup is used but it is not a substitute for reading the linked documents.

def some_fn(param1, param2, param3, param4, param5=12):
    """
    Do something.

    There are two general rules. Use a period in front of a name
    to create a hyperlink to where that name is documented,
    for example :class:`.SomeType`. Use a ~ at  the start of a
    name to remove any preceding names in the compiled
    documentation, for example
    :class:`~.module.submodule.SomeType` will only display
    "SomeType" in the compiled documentation. The modules are
    added in front of the name to resolve any ambiguity in
    name resolution, for example if two classes in your library
    have the same name but are found in different submodules.
    Periods should only be used in front of names which are part
    of your own library.

    When literal code values are specified they should be between
    two backticks, for example if I was to say that the default
    value of `param5` is ``12``. Argument names, such as
    `param5` or `param1` are surrounded by a single backtick.

    Simple code expressions, such as ``UserDefinedType(12) + 12``
    are also surrounded by two backticks. When multi-line or
    complicated code expressions are to be described, they
    should be performed in a code block.

    .. code-block:: python

        # This is a multi-line code example.
        variable_name = 'one two three'
        for i in range(10):
            print(i**2)

    Sometimes, when talking about an attribute in a class, we
    want to make it clear to the reader what attribute we are
    referring as well as the class, so the
    :attr:`.SomeTypeName.attr_name` syntax is used. However, as we
    continue to refer to the attribute, it is
    unnecessary to continue stating the class explicitly in the
    compiled documentation, so the :attr:`~.SomeTypeName.attr_name`
    syntax can be used. The ~ can be added to
    remove the class name from the compiled documentation. Use
    the ~ when appropriate to maximize the readability of the
    compiled documentation.

    Parameters
    ----------
    param1 : :class:`int`
        When the type being described is a builtin type, such as
        :class:`int` or :class:`str`, it should have the form
        the :class:`type_name`.

    param2 : :class:`.UserDefinedType`
        When the type being described refers to a user defined
        type within the same library, make sure the type name
        is preceded by a period.

    param3 : :class:`other.UserDefinedType`
        When the type being described refers to a user defined
        type in another library, state the name of the library
        and name the type. If the type is in a submodule of the
        library DO NOT name the submodules. For example, we may
        want to document the use of a :class:`numpy.ndarray`.

    param4 : :class:`~.mod.submod.subsubmod.UserDefinedType`
        Sometimes there may be two user defined types in the
        same library with the same name but defined in different
        submodules of the library. If we want to refer to form a
        hyperlink, we have to add the the necessary submodule
        names in front of the user defined type's name, so that
        it is unambiguous which type is being referred to. The
        ~ means that the submodule names will be removed from the
        compiled documentation. This means that the ~ can be
        removed or kept, depending on what is less confusing to
        the reader.

    param5 : :class:`int`, optional
        When the parameter is optional, it should be stated as
        optional after the type declaration.

    Returns
    -------
    :class:`int`
        Returns a number.

    """

    return 12

class SomeType:
    """
    An example type.

    Note that class docstrings should not document the attributes
    defined in a parent class, unless there is something different
    about them.

    Attributes
    ----------
    alpha : :class:`int`
        Attributes defined within this class are referred to by
        the :attr:`alpha` or :attr:`beta` syntax.

    beta : :class:`str`
        Attributes of a different class are referred to by
        preceding with a period and the type name, for example
        :attr:`.SomeOtherType.attribute_name`.

    """

    def __init__(self):
        self.alpha = 1
        self.beta = 'b'