Skip to content

Code Quality

Best practices for writing correct, maintainable, and well-styled Python code.

Correctness

Avoid Mutable Default Arguments

# WRONG: Default list is shared between calls
def add_item(item, lst=[]):
    lst.append(item)
    return lst

# RIGHT: Use None as default
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

Fix Late Binding in Closures

# WRONG: All functions return the last value of i
funcs = [lambda: i for i in range(3)]

# RIGHT: Capture i as default argument
funcs = [lambda x=i: x for i in range(3)]

Use is Correctly

# WRONG: Using is for value comparison
if x is 1000:  # May fail!
    pass

# RIGHT: Use == for values
if x == 1000:
    pass

# RIGHT: Use is for None, True, False
if result is None:
    pass

Handle Exceptions Properly

# WRONG: Bare except catches everything
try:
    risky_operation()
except:
    pass

# RIGHT: Catch specific exceptions
try:
    risky_operation()
except ValueError as e:
    logger.error(f"Invalid value: {e}")
except (TypeError, KeyError) as e:
    logger.error(f"Type or key error: {e}")

Write Tests

import unittest

def add(a, b):
    return a + b

class TestAdd(unittest.TestCase):
    def test_positive_numbers(self):
        self.assertEqual(add(2, 3), 5)

    def test_negative_numbers(self):
        self.assertEqual(add(-1, -1), -2)

    def test_mixed_numbers(self):
        self.assertEqual(add(-1, 1), 0)

if __name__ == '__main__':
    unittest.main()

Maintainability

Use Descriptive Names

# BAD: Cryptic names
def p(d, n):
    return d * (1 + r) ** n

# GOOD: Clear, descriptive names
def calculate_compound_interest(principal, years, rate=0.05):
    return principal * (1 + rate) ** years

# BAD: Single-letter variables
n = len(users)

# GOOD: Meaningful names
user_count = len(users)
active_user_count = len([u for u in users if u.is_active])

Keep Functions Small

# BAD: One function doing too much
def process_user_data(user):
    # Validate
    if not user.email:
        raise ValueError("No email")
    # Transform
    user.name = user.name.title()
    # Save
    db.save(user)
    # Notify
    send_email(user.email, "Welcome!")

# GOOD: Single responsibility
def validate_user(user):
    if not user.email:
        raise ValueError("No email")

def normalize_user(user):
    user.name = user.name.title()

def save_user(user):
    db.save(user)

def notify_user(user):
    send_email(user.email, "Welcome!")

def process_user(user):
    validate_user(user)
    normalize_user(user)
    save_user(user)
    notify_user(user)

Write Docstrings

def calculate_discount(price, discount_percent):
    """
    Calculate the discounted price.

    Args:
        price: Original price in dollars.
        discount_percent: Discount percentage (0-100).

    Returns:
        The discounted price.

    Raises:
        ValueError: If discount_percent is not between 0 and 100.

    Example:
        >>> calculate_discount(100, 20)
        80.0
    """
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount must be between 0 and 100")
    return price * (1 - discount_percent / 100)

Use Type Hints

from typing import List, Optional, Dict

def find_user(user_id: int) -> Optional[Dict[str, str]]:
    """Find user by ID, return None if not found."""
    pass

def get_active_users(users: List[Dict]) -> List[Dict]:
    """Filter and return only active users."""
    return [u for u in users if u.get('active')]

Style Guide (PEP 8)

Naming Conventions

# Variables and functions: snake_case
user_name = "Alice"
def calculate_total():
    pass

# Classes: PascalCase
class UserAccount:
    pass

# Constants: UPPER_SNAKE_CASE
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30

# Private: leading underscore
_internal_cache = {}
def _helper_function():
    pass

# "Private" (name mangling): double underscore
class MyClass:
    def __init__(self):
        self.__private_attr = 42

Spacing and Formatting

# Spaces around operators
x = 1 + 2
y = x * 3

# No space before colon in slices
items[1:3]
items[::2]

# Spaces after commas
func(a, b, c)
data = [1, 2, 3]

# Two blank lines between top-level definitions
def function_one():
    pass


def function_two():
    pass


class MyClass:
    pass

Import Organization

# Standard library imports
import os
import sys
from collections import defaultdict

# Third-party imports
import numpy as np
import pandas as pd

# Local imports
from mypackage import mymodule
from mypackage.utils import helper

Line Length

# Keep lines under 79-88 characters
# Break long lines appropriately

# Long function call
result = some_function(
    argument_one,
    argument_two,
    argument_three,
)

# Long string
message = (
    "This is a very long message that needs to be "
    "split across multiple lines for readability."
)

# Long condition
if (condition_one
        and condition_two
        and condition_three):
    do_something()

Code Review Checklist

Correctness

  • [ ] No mutable default arguments
  • [ ] Closures capture variables correctly
  • [ ] Exceptions handled appropriately
  • [ ] Edge cases considered
  • [ ] Tests written and passing

Maintainability

  • [ ] Functions are small and focused
  • [ ] Names are descriptive
  • [ ] Docstrings present for public APIs
  • [ ] Type hints used where helpful
  • [ ] No code duplication

Style

  • [ ] Follows PEP 8 conventions
  • [ ] Imports organized properly
  • [ ] Consistent formatting
  • [ ] Line length reasonable
  • [ ] Comments explain "why", not "what"

Summary

Aspect Key Points
Correctness Avoid mutable defaults, fix late binding, handle exceptions
Maintainability Descriptive names, small functions, good documentation
Style Follow PEP 8, consistent formatting, organized imports

Key principles: - Code is read more than written—optimize for readability - Test your code thoroughly - Follow established conventions - Keep functions focused and small - Document public APIs