# Coding Style Guide

# Indentation

Use four spaces for indentation. Do not use TAB.

# Strings

# Prefer single quotes for strings
s = 'this is a string'

# Use double quotes only if the string itself contains a single quote
s = "this ' is single quote"

# Docstrings

Always use triple quotes for docstrings.

def f():
    """This is a multi-line docstring.

    Here is some content. Docstrings partially support Markdown.
    """

def g():
    """This is a single-line docstring."""

Use natural language to describe the function's purpose. Do not enumerate each parameter and return value.

# Correct
def add(a: int, b: int):
    """Add two integers `a` and `b`."""

# Incorrect
def add(a: int, b: int):
    """
    @param a, the first argument
    @param b, the second argument
    @return, the result of a + b
    """

# Spaces

# Add a space after `,` or `:`
a, b = 1, 2
c = [1, 2, 3]
d = {'key': 'value'}

# Spaces may be added around operators
res = 1 + 2
if res < 2: pass
# Spaces can also be selectively added to indicate operator precedence
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

# Add a space after `:` in type annotations
def f(a: int, b: float): ...
def g() -> int: ...

# Add spaces around `=` when specifying default values in function parameters
def f(a: int = 1, b: int | None = None): ...
# However, omit spaces if the parameter has no type annotation
def f(a=1, b=2): pass

# Do not add spaces in keyword arguments when calling functions
print(1, 2, 3, end='', sep=',')
f(a=10, b=20)

# Naming Conventions

  • Classes: CapitalizedWords
  • Functions and variables: lower_case_with_underscores
  • Constants and enums: UPPER_CASE_WITH_UNDERSCORES or CapitalizedWords
  • Anonymous ordered variables: _0, _1, _2
  • Discarded variables: _
  • Some standard library functions: lowercase

Here are some commonly used naming conventions:

  • self: The first parameter of an instance method
  • cls: The first parameter of class methods and __new__

# Using Abbreviations

Use abbreviations only for temporary variables and internal implementations.

Abbreviations should be well-established, include key syllables of the original word, and be immediately recognizable.

  • context -> ctx (✔)
  • temporary -> tmp (✔)
  • distribution -> dist (✔)
  • visited -> vis ()
# Incorrect: Using abbreviations in public function parameters
def some_pub_fn(ctx, req_id, data):
    pass

# Correct
def some_public_function(context, request_id, data):
    pass

# Using Precise Terminology

Naming should convey precise meanings, especially when multiple synonyms exist.

For example, count, size, and length all relate to quantity, but they have different nuances:

  • count: Represents a counted value
  • length: Represents the number of elements in a container
  • size: Represents the byte size of an object
s = 'aaabc⭐'
count = s.count('a')
length = len(s)
size = len(s.encode())

print(f"{s!r} has a length of {length}, a size of {size} bytes, and contains {count} occurrences of 'a'")
# 'aaabc⭐' has a length of 6, a size of 8 bytes, and contains 3 occurrences of 'a'

# Using Professional Terminology

  • For item quantities in a game: quantity is better than item_count
  • For grid counts: area (meaning surface area) is better than grid_count

# Avoiding Built-in Names

# Incorrect: Overwriting `builtins.map`
map = [[1, 2, 3], [4, 5, 6]]
# Incorrect: Overwriting `builtins.type`
type = some_thing.type

# Internal Functions and Classes

Use a single underscore _ as a prefix for internal functions. Never use a double underscore __ (except for magic methods).

def _internal_func():
    """This is an internal function."""

class _InternalClass:
    def _internal_f(self): pass

# Importing Modules

  1. Import standard library modules first.
  2. Then import third-party dependencies.
  3. Finally, import project-specific modules.
from typing import Any
from collections import deque

from array2d import array2d

from ..utils import logger

# Coding Practices

Use is not when checking for None. Do not explicitly compare with True or False.

# Correct
if x is not None: pass

# Incorrect
if x != None: pass

# Correct
x = True
if x: pass
if not x: pass

# Incorrect
if x == True: pass
if x is True: pass
if x != False: pass

The if statement implicitly calls bool(), so it can be used to check if a container is empty.

not_empty_list = [1]
not_empty_string = '1'
truth = True

if not_empty_list:
    print('true value')

if not_empty_string:
    print('true value')

if truth:
    print('true value')

# Explicitly checking for emptiness is also valid
if len(not_empty_list) > 0: pass

# References

PEP 8 – Style Guide for Python Code