Skip to content

Pythons' Code Style Guide

:material-circle-edit-outline: ็บฆ 1048 ไธชๅญ— :fontawesome-solid-code: 114 ่กŒไปฃ็  :material-clock-time-two-outline: ้ข„่ฎก้˜…่ฏปๆ—ถ้—ด 5 ๅˆ†้’Ÿ

Getting Started

First, follow these steps to structure your repository and maintain consistency:

  1. Design a Class Diagram: Plan all key classes (e.g., DataLoader, Model, Trainer, TrainingArgs).
  2. Setting up Style Management Tools: Go to section 1 (Style Management) for instructions.
  3. Write Class Prototypes: Begin with basic implementations for each class.
  4. Submit Incremental Pull Requests (PRs): Commit small, meaningful progress and review frequently.

Next, refer to the instructions in each section below when you are writing each of them.

  1. Style Management
  2. Commit and Pull Request (PR)
  3. Prototyping
  4. File Management
  5. Imports
  6. Comments
  7. Spacing
  8. Naming
  9. Type Hints

1 - Style Management

1.1 - Install style checking

Install the pre-commit checking by pip install pre-commit

and add two files at the root directory of your repository.

file .flake8

[flake8]
docstring-convention = google
extend-ignore = E501, E731

file .pre-commit-config.yaml

# pre-commit run --all-files
repos:
-   repo: https://github.com/pycqa/flake8
    rev: 3.7.9
    hooks:
    -   id: flake8
        additional_dependencies: [
            'flake8-docstrings==1.7.0',
        ]

and finally, run pre-commit install.


1.2 - Config the Github repository

Config the branch protection rule to require review on merging each pull request.

According to this document, set up requiring at least one reviewer before merging. Managing a branch protection rule_{target="blank"}


1.3 - Set Up VS Code Style Helpers (Optional)

To streamline code formatting and import sorting, configure VS Code with the following extensions and settings:

  1. Install Extensions:

  2. Python (for Python language support).

  3. isort (for sorting imports).

  4. Configure Settings:

    To automatically sort the imports and format the code, open the settings.json and add or modify the following setting:

    // Automatically sort the imports
    "editor.codeActionsOnSave": {
        "source.organizeImports": true,
    },
    // Trim whitespace
    "files.trimTrailingWhitespace": true,
    // Add a final new line
    "files.insertFinalNewline": true,
    

2 - Commit and Pull Request (PR)

2.1 - Split Changes into Small PRs

โŒ Avoid bundling multiple unrelated modifications into a single PR.

โœ… Each PR should address a single, focused change that can be summarized in one sentence.

Examples: - Pre-commit setup as a standalone PR. - Prototyping or implementing a single class as separate PRs.


2.2 - Ensure Each PR is Complete and Meaningful

โŒ Avoid submitting partially completed features.

โœ… Submit only when a feature or change is complete and functional.


2.3 - Manage Committed Files

โŒ Avoid using git add . indiscriminately.

โœ… Use git add <specific-files> to ensure only intentional changes are included.


2.4 - Require Review Before Merging

Set up branch protection rules to require reviews before merging. Refer to the GitHub guide: Managing a branch protection rule


2.5 - PR and Commit Messages

PR Titles:

Format titles as you would for commit messages:

[label] name + summary of change

Refer to the Git Commit Style Guide for approved labels.

PR Messages:

The PR description should match the commit message format, providing clear and concise context for the changes.


3 - Prototyping

3.1 - Define Class Prototypes

A prototype is the skeleton of a class, including:

  • The class definition.
  • Function declarations with proper docstrings.

Example:

class Bar(foo):
    def __init__(self, arg: bool) -> None:
        """Comment.

        Args:
            arg      : arg.
        """
        pass

    def bar(self) -> bool:
        """Comment.

        Returns:
            arg     : arg.
        """
        return True

3.2 - Include a Main Function

Itโ€™s encouraged to add a main function to demonstrate the general workflow:

  • How classes will be instantiated.
  • How functions will be called.

Example:

int main():
    """Comment."""
    bar = Bar(True)
    return

if __name__ == "__main__":
    main()

4 - File Management

4.1 - OOP

Encapsulate each function (except main) into a related class.


4.2 - Write Prototypes First

Ensure prototypes are written before detailed implementations.


4.3 - Avoid Hyphen or Underscore in File Naming

Use folders to organize files instead of naming files with hyphens or underscores.

โœ… Do: models/models.py

โŒ Don't: models_main.py


Imports

5.1 - Order

  1. Import all Python standard libraries, in alphabetical order (based on the package name instead of the folder name), and add an empty line.
  2. Import all 3rd-party libraries, in alphabetical order, and add an empty line.
  3. Import all libraries defined within the project.

Example:

import os

import torch
import transformers

from myfile import MyClass

If you have set up the automatical sorting tool in VSCode, it can avoid you from singing an alphabet song every time you add an import.


5.2 - Importing Names

โŒ Don't use from x import y.

โœ… The only examptions are those well-established ones, e.g., from typing import Dict, List, Optional, Sequence, Union.

โœ… Do use import x and x.y to avoid namespace conflicts, for example:

import torch
import transformers

torch.nn.Module
transformers.Trainer

Comments

6.1 - Where Should Comments Appear?

  • File docstring
  • Class docstring
  • Function docstring
  • Other (full-line comment, inline comment)

6.2 - Formats

  • Required:
  • File docstring (at the top of the file)
  • Class docstring (immediately after the class name)
  • Function docstring (immediately after the function name)

  • Docstrings can be either a sentence or multiple paragraphs.

    Example for a single sentence:

    """A sentence describing the file content."""
    

    Example for multiple paragraphs:

    """A sentence describing the file content.
    
    Declarations.
    """
    
  • Function docstring format:

    """A sentence describing the function's content.
    
    Args:
        arg1    : explanation
    
    Returns:
        return1 : explanation
    """
    

6.3 - File Docstring

Ensure there is no space between the docstring and the imports.


6.4 - Be Sure to Make Every Doc a 'Sentence'

โœ… Do # a comment.

โŒ Don't # a comment..


6.5 - Keep Consistency in Omitting 'The'

โœ… Do

# comment 1.
# comment 2.
# comment 3.

โŒ Don't

# comment 1.
# the comment 2.
# comment 3.

Spacing

  • In every comment: Add 1 empty line between each section.
  • Except at the top of the file, do not use more than 2 empty lines.

If you have configured the styling tool in VSCode, it will automatically manage the spacing for you.


Naming

8.1 - Avoid Abbreviations and Use Full Spellings

โœ… Do path_checkpoint.

โŒ Don't path_ckpt.


8.2 - Naming Conventions (My Own Habit)

  • Class Names: Use a noun-adjective format.
  • Function Names: Use a verb-noun format.
  • Functions and files that perform similar tasks or operate on the same objects should begin with the same substring.

8.3 - Private Members

Class private members should start with an underscore _ to distinguish them from input parameters.

e.g., the correct private number name should be

def __init__(self, foo):
    self._foo = foo     # recommended

instead of

def __init__(self, foo):
    self.foo = foo      # not recommended

8.4 - Accessing Private Members

Avoid directly accessing private members outside the class.

class Foo:
    def __init__(self, bar):
        self._bar = bar

    def get_bar(self):
        return self._bar


foo = Foo()
foo._bar                # not recommended, since the members should be 'considered as private'.
foo.get_bar()           # not recommended, since the function name looks messy.

Instead, using a @property decorator is recommonded.

class Foo:
    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        return self._bar

foo = Foo()
foo.bar                 # recommended

Type Hints

9.1 - Write Type Hints

โœ… Do: write type hints in function declarations.

def foo(bar: int) -> str:
    """
    Args:
        bar:
    Returns:
        str:
    """
    pass

โŒ Don't: write types in the comment.

def foo(bar: int) -> str:
    """
    Args:
        bar (int):
    Returns:
        str (str):
    """
    pass

9.2 - Use the typing Library

Use the typing library when you need to indicate the element type of a Dict, List, etc.

from typing import Dict

typing.Dict[str, Union[int, float]]

Commonly used examples:

  • Dict[x, y] โ€” for dictionaries with keys of type x and values of type y.
  • Union[x, y, ...] โ€” for a type that can be one of several types (e.g., Union[int, float]).
  • List[x] โ€” for a list where each element is of type x.
  • Tuple[x, y, ...] โ€” for a tuple with fixed types and length.
  • Optional[x] โ€” equivalent to Union[x, None], meaning a value could be of type x or None (e.g., Optional[str]).
  • Callable[[x, y], z] โ€” for a function type that takes arguments of types x and y, and returns a value of type z.
  • Any โ€” for any type (used when you don't want to specify a type).

More examples in typing โ€” Support for type hints


References and Credits

This code style guide is based on the Google Python Style Guide.

Written with the help of GPT-4o.