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.py
yapf (Yet Another Python Formatter)
Google's formatter. More configurable than Black but less opinionated:
pip install yapf
yapf --in-place my_script.py
Which 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.