Indentation as Structure
Python uses indentation (whitespace at the start of a line) to define code blocks. This is not optional formatting - it is the syntax. The standard is 4 spaces per level.
# The colon : starts a new block; the indented lines ARE the block
if True:
print("indented - inside the if block")
print("also inside the if block")
print("back to top level - outside the if")
# Functions use the same pattern
def greet(name):
message = f"Hello, {name}!" # 4 spaces in
return message # 4 spaces in
# Nested blocks - each level adds 4 more spaces
for i in range(3):
if i % 2 == 0:
print(f"{i} is even") # 8 spaces in
else:
print(f"{i} is odd") # 8 spaces in
# Indentation error examples
# if True:
# print("wrong") # IndentationError: expected an indented block
#
# if True:
# print("ok")
# print("too much indent") # IndentationError: unexpected indent
Python 3 raises a TabError if you mix tabs and spaces for indentation. Configure your editor to insert 4 spaces when you press Tab for Python files. In VS Code: bottom-right status bar shows "Spaces: 4" when correctly configured for Python.
Statements and Lines
# One statement per line - no semicolons needed
x = 10
y = 20
z = x + y
# Multiple statements per line using ; (avoid in practice)
a = 1; b = 2; c = 3 # works but PEP 8 discourages it
# Line continuation with backslash \
total = 1 + 2 + 3 + \
4 + 5 + 6 # treated as one long line
# Implicit continuation inside brackets (preferred)
result = (1 + 2 + 3 +
4 + 5 + 6) # parentheses allow continuation without \
# Also works inside [] and {}
fruits = [
"apple",
"banana",
"cherry",
] # trailing comma is fine and encouraged
config = {
"host": "localhost",
"port": 5432,
"database": "mydb",
}
# Function calls with many arguments
result = some_function(
argument_one,
argument_two,
keyword=value,
)
# pass - placeholder for empty blocks (functions, classes, loops)
def todo():
pass # not implemented yet
class EmptyClass:
pass
Naming Conventions
Python has well-established naming conventions defined in PEP 8. Following them makes your code recognizable to any Python developer.
| Type | Convention | Example |
|---|---|---|
| Variables | snake_case | user_name, total_price |
| Functions | snake_case | get_user(), calculate_total() |
| Constants | UPPER_SNAKE_CASE | MAX_SIZE, API_KEY |
| Classes | PascalCase | UserProfile, HttpClient |
| Modules | lowercase (short) | utils, config |
| Packages | lowercase (short, no underscore) | mypackage |
| Private (by convention) | _leading_underscore | _internal_method |
| Name mangling | __double_leading | __private_attr |
| Dunder methods | __double_both__ | __init__, __str__ |
# Variables and functions: snake_case
user_name = "Alice"
total_score = 95
def calculate_average(scores):
return sum(scores) / len(scores)
# Constants: UPPER_SNAKE_CASE
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
# Classes: PascalCase
class UserAccount:
def __init__(self, username):
self.username = username # public attribute
self._cache = {} # private by convention
self.__secret = "hidden" # name-mangled (harder to access from outside)
def get_username(self): # public method
return self.username
def _validate(self): # private by convention
pass
# _ alone is used for throwaway variables
for _ in range(5):
print("hello") # _ means "I don't need this loop variable"
x, _, z = (1, 2, 3) # ignore the middle value
PEP 8 Style Guide
PEP 8 is the official Python style guide. Most Python projects enforce it. The key rules:
# Imports: one per line, at the top, grouped
import os # standard library
import sys
import requests # third-party (blank line separates groups)
from myapp import utils # local imports (blank line separates)
# NOT recommended:
# import os, sys (multiple on one line)
# import sys, os (wrong order)
# Blank lines:
# 2 blank lines before/after top-level functions and classes
# 1 blank line between methods inside a class
class MyClass:
def method_one(self):
pass
def method_two(self): # 1 blank line between methods
pass
def top_level(): # 2 blank lines before top-level definition
pass
# Spaces around operators
x = 10 # NOT x=10
y = x + 1 # NOT x+1 or x +1
z = x * 2 + y # spaces around all operators
# No spaces inside brackets
print(x) # NOT print( x )
fruits[0] # NOT fruits[ 0 ]
d = {"k": "v"} # NOT d = { "k" : "v" }
# Spaces after commas, colons in dicts
result = func(1, 2, 3) # NOT func(1,2,3)
d = {"key": "value"} # NOT d = {"key" :"value"}
# Max line length: 79 chars (88 with Black)
# Automate: pip install black; black myfile.py
Whitespace Rules
# Comparison operators - spaces on both sides
if x == 10:
pass
if x is None:
pass
if x is not None:
pass
# Default argument values - NO spaces around = in function signatures
def connect(host, port=5432, timeout=30): # correct
pass
def connect(host, port = 5432): # wrong (PEP 8 violation)
pass
# But spaces in keyword arguments when calling:
connect(host="localhost", port=5432) # OK
# Or without spaces:
connect(host="localhost", port=5432) # both are acceptable for keyword args
# Avoid extra whitespace
spam(ham[1], {eggs: 2}) # correct
spam( ham[ 1], { eggs: 2}) # wrong
# Trailing whitespace - delete it (editors can do this automatically)
# Blank lines at end of file - exactly 1
Type Hints (Introduction)
Python 3.5+ supports optional type hints. They do not affect runtime behavior but help IDEs, type checkers (mypy), and other developers understand your code.
# Variable annotation
name: str = "Alice"
age: int = 30
price: float = 19.99
active: bool = True
# Function with type hints
def greet(name: str) -> str:
return f"Hello, {name}!"
def add(a: int, b: int) -> int:
return a + b
# Type hints don't enforce types at runtime
def process(x: int) -> None:
print(x)
process("not an int") # runs fine - no error at runtime
# But mypy or Pylance will flag this as a type error
# Type hints are documented here; full coverage in the Type Hints lesson
# from typing import Optional, List, Dict
# def find(items: List[str], key: str) -> Optional[str]: ...
Black is the most widely used Python auto-formatter. It applies PEP 8 and its own opinionated rules consistently: pip install black, then black yourfile.py. Configure VS Code to run Black on save. You will never argue about formatting again.