Python Style Guide
2024-06-10T19:03:35.404073540ZIdentifier 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~.