Skip to content
Python by Example

Best Practices

PEP 8 - Python Style Guide

10_best_practices/01_pep8.py

This file demonstrates PEP 8, the official Python style guide. Following PEP 8 makes your code more readable, consistent, and professional. These are conventions, not strict rules, but following them is highly recommended.

"""
================================================================================
File: 01_pep8.py
Topic: PEP 8 - Python Style Guide
================================================================================
 
This file demonstrates PEP 8, the official Python style guide. Following PEP 8
makes your code more readable, consistent, and professional. These are
conventions, not strict rules, but following them is highly recommended.
 
Key Concepts:
- Indentation and whitespace
- Naming conventions
- Line length and wrapping
- Imports organization
- Comments and docstrings
 
Reference: https://peps.python.org/pep-0008/
 
================================================================================
"""
 
# =============================================================================
# 1. INDENTATION
# =============================================================================
# Use 4 spaces per indentation level. Never mix tabs and spaces.
 
# GOOD
def function_with_proper_indentation():
    if True:
        for i in range(10):
            print(i)
 
# Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
                         var_three, var_four)
 
# Hanging indent (add a level)
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)
 
# =============================================================================
# 2. LINE LENGTH
# =============================================================================
# Limit lines to 79 characters (72 for docstrings/comments)
 
# GOOD - Use implicit line continuation
total = (first_variable
         + second_variable
         + third_variable)
 
# GOOD - Use backslash when necessary
with open('/very/long/path/to/file.txt') as file_one, \
     open('/another/long/path/to/file.txt') as file_two:
    pass
 
# =============================================================================
# 3. BLANK LINES
# =============================================================================
# - 2 blank lines around top-level functions and classes
# - 1 blank line between methods in a class
 
 
class FirstClass:
    """First class."""
    pass
 
 
class SecondClass:
    """Second class."""
    
    def method_one(self):
        """First method."""
        pass
    
    def method_two(self):
        """Second method."""
        pass
 
 
def top_level_function():
    """A top-level function."""
    pass
 
 
# =============================================================================
# 4. IMPORTS
# =============================================================================
# - One import per line
# - Group in order: standard library, third-party, local
# - Use absolute imports
 
# GOOD
import os
import sys
from typing import List, Optional
 
# Third party imports (after blank line)
# import numpy as np
# import pandas as pd
 
# Local imports (after blank line)
# from mypackage import mymodule
 
# BAD - Multiple imports on one line
# import os, sys
 
# =============================================================================
# 5. WHITESPACE
# =============================================================================
 
# GOOD - No extra whitespace inside brackets
spam = [1, 2, 3]
ham = {"key": "value"}
eggs = (1,)
 
# BAD
# spam = [ 1, 2, 3 ]
# ham = { 'key': 'value' }
 
# GOOD - One space around operators
x = 1
y = 2
z = x + y
 
# BAD - No space around = in keyword arguments
# def function(x, y = 5):
def function(x, y=5):
    pass
 
# GOOD - Space after comma
some_list = [1, 2, 3]
 
# GOOD - No space before colon in slices
some_list[1:3]
some_list[::2]
 
# =============================================================================
# 6. NAMING CONVENTIONS
# =============================================================================
 
# Variables and functions: lowercase_with_underscores (snake_case)
user_name = "John"
total_count = 42
 
def calculate_average(numbers):
    return sum(numbers) / len(numbers)
 
# Constants: UPPERCASE_WITH_UNDERSCORES
MAX_BUFFER_SIZE = 4096
DEFAULT_TIMEOUT = 30
PI = 3.14159
 
# Classes: CapitalizedWords (PascalCase)
class UserAccount:
    pass
 
class HttpConnection:
    pass
 
# Private: prefix with underscore
_internal_variable = "private"
def _internal_function():
    pass
 
class MyClass:
    def _protected_method(self):
        """Single underscore = protected (convention)."""
        pass
    
    def __private_method(self):
        """Double underscore = name mangling (truly private)."""
        pass
 
# =============================================================================
# 7. COMMENTS
# =============================================================================
 
# GOOD - Inline comments have at least 2 spaces before #
x = 5  # This is an inline comment
 
# Block comments precede the code they describe
# This is a block comment that explains
# the following piece of code.
complex_calculation = 1 + 2 + 3
 
# Don't state the obvious
# BAD: x = 5  # Assign 5 to x
# GOOD: x = 5  # Default timeout in seconds
 
# =============================================================================
# 8. DOCSTRINGS
# =============================================================================
 
def example_function(param1, param2):
    """
    Brief description of the function.
    
    Longer description if needed. Explain what the function
    does, not how it does it.
    
    Args:
        param1: Description of first parameter
        param2: Description of second parameter
    
    Returns:
        Description of what is returned
    
    Raises:
        ValueError: When param1 is negative
    
    Example:
        >>> example_function(1, 2)
        3
    """
    return param1 + param2
 
 
class ExampleClass:
    """Brief description of the class.
    
    Longer description of the class including its purpose
    and usage patterns.
    
    Attributes:
        attr1: Description of first attribute
        attr2: Description of second attribute
    """
    
    def __init__(self, attr1, attr2):
        """Initialize ExampleClass."""
        self.attr1 = attr1
        self.attr2 = attr2
 
# =============================================================================
# 9. COMPARISON AND BOOLEAN
# =============================================================================
 
# Use 'is' and 'is not' for None comparisons
# GOOD
x = None
if x is None:
    pass
 
# BAD
# if x == None:
 
# Use 'is not' instead of 'not ... is'
# GOOD
if x is not None:
    pass
 
# BAD
# if not x is None:
 
# Don't compare boolean values with == or !=
# GOOD
flag = True
if flag:
    pass
 
# BAD
# if flag == True:
# if flag is True:  # Only use when testing identity
 
# =============================================================================
# 10. EXCEPTION HANDLING
# =============================================================================
 
# Catch specific exceptions
# GOOD
try:
    value = int(user_input)
except ValueError:
    print("Invalid input")
 
# BAD - Too broad
# try:
#     value = int(user_input)
# except:  # or except Exception:
#     print("Error")
 
# Use 'raise' to re-raise exception
try:
    process_data()
except ValueError:
    logger.error("Bad value")
    raise
 
# =============================================================================
# 11. FUNCTION ANNOTATIONS (Type Hints)
# =============================================================================
 
def greeting(name: str) -> str:
    """Return a greeting."""
    return f"Hello, {name}!"
 
# Complex types
from typing import List, Dict, Optional
 
def process_items(
    items: List[str],
    config: Dict[str, int],
    default: Optional[str] = None
) -> bool:
    """Process items with configuration."""
    return True
 
# =============================================================================
# 12. SUMMARY: KEY RULES
# =============================================================================
 
print("PEP 8 Summary - Key Rules:")
print("""
1. Use 4 spaces for indentation
2. Limit lines to 79 characters
3. Use blank lines to separate functions and classes
4. Organize imports: standard lib, third-party, local
5. Use snake_case for functions and variables
6. Use PascalCase for classes
7. Use UPPER_CASE for constants
8. Use spaces around operators and after commas
9. Write docstrings for all public modules, functions, classes
10. Compare with 'is' for None, use bool directly
""")
 
# Use flake8 or black to automatically check/format code
print("Tools to help with PEP 8:")
print("  - flake8: Check for PEP 8 violations")
print("  - black: Automatic code formatting")
print("  - isort: Sort imports automatically")
print("  - pylint: Comprehensive code analysis")