Python Style Guide

2024-06-10T19:03:35.404073540Z
pythonstyle guidecomputer science

Identifier Cases

- Classes :: PascalCase
- Methods and Functions :: snake_case
- Variables :: snake_case
- Constants and Enum Variants :: SCREAMING_SNAKE_CASE
- Modules :: snake_case

Identifier Naming

Variable Names

Should describe the data they hold.

user_ids = [1, 2, 3]

instance_members = instance.get_members()

Method and Function Names

def fetch_users() -> list[User]:
    ...


def get_installed_apt_packages() -> list[Package]:
    ...


def copy_package_files_to_files_dir() -> None:
    ...

Blocks

Blocks should have an empty line of whitespace before and after them, unless preceded by a block opening or succeeded by a block ending. There should be a blank line between blocks in the same structure.

def disconnect_users(users: list[User | None]) -> None:
    for user in users:
        if user is None:
            return

        if not user.is_admin():
            user.disconnect()

        if user.is_banned():
            user.delete()

        elif user.is_muted():
            muted_users.append(user.id)

    muted_users.commit()

Other Practices

Early Returns

Early returns should be used wherever possible. (where an early return means ~return~, ~break~, or ~continue~)

class Game:
    ...

    def choose_fates(self, characters: list[Character], max_fate_count: int = 3) -> list[tuple[Character, list[Fate]]]:
        character_fates = []

        for character in characters:
            chosen_fates = []

            for _ in range(max_fate_count):
                danger = character.danger
                focus = random.randint(round(danger * 0.75), round(danger * 1.25))
                
                if focus not in self.fates:
                    continue

                chosen_fates.append(self.options[focus])
           
            character_fates.append((character, chosen_fates))

        return character_fates

    ...


def choose_response(message: discord.Message) -> str:
    if message.channel is None:
        return "This command does not work in DMs."  # No, this doesn't make sense to do
                                                     # in reality because this *would* work in DMs.

    return random.choice([
        "Hello!",
        "Welcome!",
        ...
    ])

Typehints

Every return type and every argument type should be typehinted. Variables should be typehinted where it provides clarity or the typechecker requests it. If a attribute type cannot be inferred by the typechecker, it should be specified. ~typing.Any~ should almost never be used, in most cases, a ~typing.TypeVar~ would work better or you can specify a ~typing.Union~ of types that it may be. It is rarely, if ever, /any/ type.

Recommendation: Tuples should be used instead of lists when you have fixed-length lists (such as those written into the code to replace ~somethingN~ variables).
Strong Recommendation: Dictionaries with known fields and types should be replaced with ~dataclasses.dataclass~ or ~typing.TypedDict~.