Code Formatting & Layout
Well-formatted code is easier to read, easier to review, and easier to maintain. Python's PEP 8 style guide provides clear rules for formatting, and modern tools can enforce them automatically.
Indentation: 4 Spaces, Always
Python uses indentation to define code blocks. The standard is 4 spaces per level — never tabs:
# Correct: 4 spaces def greet(name): if name: message = f"Hello, {name}!" print(message) else: print("Hello, stranger!") # Wrong: tabs, 2 spaces, or mixed def greet(name): if name: # 2 spaces — not standard print(name) # mixed indentation — will cause errors
Why not tabs? Tabs display differently depending on the editor (2, 4, or 8 spaces wide). Spaces look the same everywhere. Python 3 actually forbids mixing tabs and spaces within the same block.
Click "Run" to execute your codeContinuation Indentation
When a function call or definition spans multiple lines, align arguments or use a hanging indent:
# Option 1: Align with opening delimiter result = some_function(arg_one, arg_two, arg_three, arg_four) # Option 2: Hanging indent (4 spaces from the base) result = some_function( arg_one, arg_two, arg_three, arg_four, ) # Option 3: Hanging indent for function definitions def long_function_name( param_one, param_two, param_three, param_four, ): print(param_one)
Line Length
PEP 8 recommends a maximum of 79 characters per line. In practice, many projects use 99 or 120 as a more comfortable limit:
| Standard | Line Limit | Common Usage |
|---|---|---|
| PEP 8 strict | 79 | Standard library, open source |
| Black default | 88 | Projects using Black formatter |
| Common relaxed | 99-120 | Internal/company projects |
Breaking Long Lines
When a line is too long, you have two options:
# Option 1: Implicit continuation with parentheses (preferred) total = ( first_variable + second_variable + third_variable ) # Also works with function calls result = some_function( argument_one, argument_two, argument_three, ) # Option 2: Backslash continuation (less preferred) total = first_variable \ + second_variable \ + third_variable
Implicit continuation with parentheses is preferred because backslashes are fragile — a space after the backslash silently breaks the continuation.
Click "Run" to execute your codeBlank Lines
Blank lines separate logical sections of code and improve readability:
- 2 blank lines before and after top-level definitions (functions, classes)
- 1 blank line between methods inside a class
- 1 blank line to separate logical sections within a function (sparingly)
import os class FileProcessor: """Process files from a directory.""" def __init__(self, directory): self.directory = directory def list_files(self): return os.listdir(self.directory) def process(self): for f in self.list_files(): self._handle_file(f) def _handle_file(self, filename): print(f"Processing: {filename}") def helper_function(): """A standalone function, separated by 2 blank lines.""" pass
Import Ordering
Imports should appear at the top of the file and be grouped in a specific order, with a blank line between each group:
- Standard library imports
- Third-party imports
- Local application imports
# Group 1: Standard library import os import sys from pathlib import Path from collections import defaultdict # Group 2: Third-party packages import requests from flask import Flask, jsonify from sqlalchemy import create_engine # Group 3: Local imports from myapp.config import Settings from myapp.models import User
Import Style
Prefer import module for top-level modules and from module import name when you need specific items:
# Good: import the module import os import json # Good: import specific names when it improves readability from pathlib import Path from collections import defaultdict, OrderedDict from typing import Optional, List # Avoid: wildcard imports (pollutes namespace, hides where names come from) from os import * # Bad from typing import * # Bad # Avoid: importing too many names from one module from os import path, getcwd, listdir, mkdir, remove, rename # Consider: import os
Tip: The
isorttool automatically sorts and groups your imports according to these rules. Most formatters like Black integrate with it.
Click "Run" to execute your codeWhitespace Rules
Around Operators
Use a single space around assignment and comparison operators:
# Good x = 5 y = x + 3 is_valid = x > 0 and y < 10 # Bad x=5 y = x+3 is_valid = x>0 and y<10
Exception: Default Arguments and Keyword Arguments
Don't use spaces around = in function parameters:
# Good def connect(host="localhost", port=8080, timeout=30): pass connect(host="example.com", port=443) # Bad def connect(host = "localhost", port = 8080): pass
After Commas
Always put a space after commas, not before:
# Good numbers = [1, 2, 3, 4, 5] result = calculate(x, y, z) # Bad numbers = [1,2,3,4,5] numbers = [1 , 2 , 3]
Inside Brackets
Don't add spaces immediately inside parentheses, brackets, or braces:
# Good numbers = [1, 2, 3] data = {"key": "value"} result = func(arg) # Bad numbers = [ 1, 2, 3 ] data = { "key": "value" } result = func( arg )
Colons in Slices
No spaces around colons in slices (but spaces around colons in dicts):
# Good numbers[1:3] numbers[::2] data = {"key": "value"} # Bad numbers[1 : 3] numbers[ ::2 ]
Click "Run" to execute your codeTrailing Commas
Use trailing commas in multi-line data structures. This makes diffs cleaner when items are added:
# Good: trailing comma fruits = [ "apple", "banana", "cherry", # <-- trailing comma ] # Without trailing comma, adding "date" changes two lines in the diff # With trailing comma, adding "date" only changes one line
Auto-Formatters
Instead of manually enforcing all these rules, let a tool do it. The three most popular Python formatters:
Black
The most popular formatter. It's opinionated and has almost no configuration — which is the point. "Any color you like, as long as it's black."
# Install pip install black # Format a file black my_script.py # Format an entire project black src/
Black uses 88 characters as its default line length and makes all formatting decisions for you. This eliminates style debates in code reviews.
autopep8
A more conservative formatter that only fixes PEP 8 violations:
pip install autopep8
autopep8 --in-place my_script.pyyapf (Yet Another Python Formatter)
Google's formatter. More configurable than Black but less opinionated:
pip install yapf
yapf --in-place my_script.pyWhich One to Choose?
| Formatter | Philosophy | Config | Line Length |
|---|---|---|---|
| Black | Opinionated, minimal config | Almost none | 88 |
| autopep8 | Fix PEP 8 violations only | Moderate | 79 |
| yapf | Configurable, reformats all code | Extensive | Configurable |
Recommendation: Start with Black. Its lack of configuration means your entire team writes identically formatted code with zero debate. Pair it with isort for import sorting.
Putting It All Together
Here's a well-formatted Python file that follows all the conventions:
"""User management utilities. Provides functions for creating, validating, and formatting user data. """ import re from datetime import datetime from typing import Optional from myapp.database import get_connection from myapp.exceptions import ValidationError MAX_USERNAME_LENGTH = 30 MIN_PASSWORD_LENGTH = 8 class User: """Represents a registered user.""" def __init__(self, username: str, email: str): self.username = username self.email = email self.created_at = datetime.now() def display_name(self) -> str: return self.username.title() def validate_username(username: str) -> bool: """Check if a username meets requirements.""" if not username: return False if len(username) > MAX_USERNAME_LENGTH: return False return bool(re.match(r"^[a-zA-Z0-9_]+$", username)) def create_user( username: str, email: str, password: Optional[str] = None, ) -> User: """Create a new user after validation.""" if not validate_username(username): raise ValidationError(f"Invalid username: {username}") return User(username, email)
This example demonstrates proper indentation, blank lines, import ordering, whitespace, trailing commas, and line length management.