The Python Cheat Sheet: Syntax, Data Structures, and Patterns on One Page

Python rewards developers who know its core syntax, built-in data structures, and idiomatic patterns by heart. This cheat sheet condenses all three into one reference. It covers variables, control flow, comprehensions, functions, classes, and the tools that separate readable Python from fragile scripts. Every snippet runs on Python 3.10+ and follows PEP 8.

Quick Takeaways

  • Lists are ordered and mutable, tuples are ordered and immutable, sets are unordered and unique, dicts map keys to values in insertion order.
  • List comprehensions replace most for loops that build collections, and run faster than manual .append() loops.
  • Use dataclasses, type hints, and context managers (with) to write production-grade code with less boilerplate.
  • Pick the structure by access pattern: O(1) lookups need a dict or set, not a list.
Structure Syntax Ordered Mutable Duplicates Lookup
list [1, 2, 3] Yes Yes Yes O(n)
tuple (1, 2, 3) Yes No Yes O(n)
set {1, 2, 3} No Yes No O(1)
dict {"a": 1} Yes (3.7+) Yes Unique keys O(1)
frozenset frozenset({1, 2}) No No No O(1)

Core Syntax Breakdown

Variables and Data Types

Python is dynamically typed. A variable is a name bound to an object, not a typed memory slot.

name = "Ada"          # str
age = 36              # int
height = 1.72         # float
is_active = True      # bool
nothing = None        # NoneType

# Multiple assignment and swapping
x, y = 10, 20
x, y = y, x           # No temp variable needed

After the swap, x is 20 and y is 10. Python builds a tuple on the right-hand side, then unpacks it.

Type Hints

Type hints do not enforce types at runtime. Tools like mypy and pyright use them to catch bugs before execution.

def greet(name: str, times: int = 1) -> str:
    """Return a repeated greeting."""
    return " ".join(f"Hello, {name}!" for _ in range(times))

print(greet("Ada", 2))

Output: Hello, Ada! Hello, Ada!

Strings and f-Strings

language = "Python"
version = 3.13

print(f"{language} {version}")       # Python 3.13
print(f"{version:.1f}")              # 3.1 (format spec)
print(f"{language!r}")               # 'Python' (repr)
print(f"{1_000_000:,}")              # 1,000,000
print(f"{language=}")                # language='Python' (debug)

Common string methods:

Method Purpose Example
.strip() Trim whitespace " hi ".strip() → "hi"
.split() String to list "a,b".split(",") → ["a", "b"]
.join() List to string "-".join(["a", "b"]) → "a-b"
.replace() Substitute "cat".replace("c", "b") → "bat"
.startswith() Prefix check "python".startswith("py") → True

Control Flow

Conditionals and Pattern Matching

score = 82

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
else:
    grade = "C"

# Ternary expression
status = "pass" if score >= 60 else "fail"

Structural pattern matching (match/case, Python 3.10+) handles shape-based branching:

def handle(event: dict) -> str:
    match event:
        case {"type": "click", "x": x, "y": y}:
            return f"Click at ({x}, {y})"
        case {"type": "key", "key": "q"}:
            return "Quit"
        case {"type": "key", "key": key}:
            return f"Key: {key}"
        case _:
            return "Unknown"

print(handle({"type": "click", "x": 4, "y": 9}))

Output: Click at (4, 9). The first matching case wins, and _ is the wildcard fallback.

Loops

fruits = ["apple", "banana", "cherry"]

# enumerate gives index and value
for i, fruit in enumerate(fruits, start=1):
    print(i, fruit)

# zip pairs iterables
prices = [1.2, 0.5, 3.0]
for fruit, price in zip(fruits, prices, strict=True):
    print(f"{fruit}: ${price}")

The strict=True flag (3.10+) raises ValueError if the iterables differ in length. Use it to catch silent data loss.

Data Structures in Depth

Lists

nums = [5, 2, 8, 1]
nums.append(9)         # add to end: O(1)
nums.insert(0, 7)      # add at index: O(n)
nums.pop()             # remove last: O(1)
nums.sort()            # in place: O(n log n)
sorted_desc = sorted(nums, reverse=True)  # new list

print(nums[1:3])       # slice -> [2, 5]
print(nums[::-1])      # reversed copy

Tuples and Unpacking

point = (3, 4)
x, y = point

first, *rest = [1, 2, 3, 4]
print(first, rest)     # 1 [2, 3, 4]

Tuples are hashable when their contents are hashable, so they work as dict keys.

Dictionaries

user = {"name": "Ada", "role": "admin"}

user["email"] = "ada@example.com"
print(user.get("phone", "N/A"))   # safe access with default

for key, value in user.items():
    print(key, value)

# Merge (3.9+)
defaults = {"theme": "dark", "lang": "en"}
config = defaults | {"lang": "fr"}
print(config)   # {'theme': 'dark', 'lang': 'fr'}

Use .get() instead of [] when a key may be missing. It avoids KeyError.

Sets

a = {1, 2, 3, 4}
b = {3, 4, 5}

print(a | b)   # union: {1, 2, 3, 4, 5}
print(a & b)   # intersection: {3, 4}
print(a - b)   # difference: {1, 2}
print(a ^ b)   # symmetric difference: {1, 2, 5}

# Deduplicate while losing order
unique = list(set([1, 2, 2, 3]))
# Deduplicate while keeping order
ordered_unique = list(dict.fromkeys([3, 1, 3, 2, 1]))

The collections Module

from collections import Counter, defaultdict, deque

# Count occurrences
print(Counter("mississippi").most_common(2))  # [('i', 4), ('s', 4)]

# Auto-initialize missing keys
groups = defaultdict(list)
for word in ["apple", "avocado", "banana"]:
    groups[word[0]].append(word)
print(dict(groups))  # {'a': ['apple', 'avocado'], 'b': ['banana']}

# O(1) appends and pops from both ends
queue = deque([1, 2, 3])
queue.appendleft(0)
queue.pop()

Use deque for queues. list.pop(0) costs O(n), while deque.popleft() costs O(1).

Comprehensions and Generators

# List comprehension
squares = [n**2 for n in range(10) if n % 2 == 0]

# Dict comprehension
lengths = {word: len(word) for word in ["a", "bb", "ccc"]}

# Set comprehension
initials = {w[0] for w in ["apple", "avocado", "banana"]}

# Generator expression: lazy, O(1) memory
total = sum(n**2 for n in range(1_000_000))

A comprehension in brackets builds the entire list in memory. A generator expression in parentheses yields one item at a time. Prefer generators when you only iterate once.

Functions

def connect(host: str, *, port: int = 5432, timeout: float = 5.0) -> str:
    """Keyword-only args after the bare `*`."""
    return f"{host}:{port} (timeout={timeout}s)"

def total(*args: int, **kwargs: int) -> int:
    """*args collects positionals; **kwargs collects keywords."""
    return sum(args) + sum(kwargs.values())

print(connect("db.local", port=6543))
print(total(1, 2, bonus=10))   # 13

Mutable Default Argument Trap

# Bad: the list is created once and shared across calls
def add_item(item, bucket=[]):
    bucket.append(item)
    return bucket

# Good: use None as a sentinel
def add_item_safe(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

Decorators

import functools
import time

def timed(func):
    @functools.wraps(func)   # preserves __name__ and docstring
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timed
def slow_sum(n: int) -> int:
    return sum(range(n))

slow_sum(5_000_000)

The decorator prints the elapsed time, then returns the original result. Always apply functools.wraps so introspection and debugging still work.

Classes and Dataclasses

from dataclasses import dataclass, field

@dataclass(slots=True, frozen=True)
class Point:
    x: float
    y: float

    def distance_to(self, other: "Point") -> float:
        return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5

@dataclass
class Order:
    id: int
    items: list[str] = field(default_factory=list)

p1, p2 = Point(0, 0), Point(3, 4)
print(p1.distance_to(p2))   # 5.0

@dataclass generates __init__, __repr__, and __eq__ automatically. frozen=True makes instances immutable and hashable. slots=True cuts per-instance memory overhead.

Error Handling and Context Managers

from pathlib import Path

def read_config(path: str) -> str:
    try:
        return Path(path).read_text(encoding="utf-8")
    except FileNotFoundError:
        return ""
    except PermissionError as exc:
        raise RuntimeError(f"Cannot read {path}") from exc
    else:
        pass          # runs only if no exception occurred
    finally:
        print("Done")  # always runs

Custom context manager:

from contextlib import contextmanager

@contextmanager
def opened(path: str):
    f = open(path, encoding="utf-8")
    try:
        yield f
    finally:
        f.close()

with opened("notes.txt") as f:
    print(f.readline())

Performance Comparison: Choosing the Right Tool

Task Slow Choice Fast Choice Complexity Gain
Membership test x in list x in set O(n) → O(1)
Queue pop from front list.pop(0) deque.popleft() O(n) → O(1)
String building s += "x" in loop "".join(parts) O(n²) → O(n)
Counting items Manual dict loop Counter(items) Cleaner and C-optimized
Sum of large range List then sum() Generator in sum() O(n) memory → O(1)

Best Practices: Anti-Patterns vs. Refactored Code

Anti-Pattern 1: Manual Index Loops

# Bad
names = ["Ada", "Linus", "Guido"]
for i in range(len(names)):
    print(i, names[i])
# Good
for i, name in enumerate(names):
    print(i, name)

Anti-Pattern 2: Bare except

# Bad: hides bugs and swallows KeyboardInterrupt
try:
    risky()
except:
    pass
# Good: catch specific errors and log them
import logging

try:
    risky()
except ValueError as exc:
    logging.warning("Invalid value: %s", exc)

Anti-Pattern 3: Building Strings in a Loop

# Bad: creates a new string each iteration
result = ""
for word in words:
    result += word + " "
# Good: a single allocation
result = " ".join(words)

Anti-Pattern 4: Opening Files Without with

# Bad: file may stay open if an error occurs
f = open("data.txt")
data = f.read()
f.close()
# Good: automatic cleanup
with open("data.txt", encoding="utf-8") as f:
    data = f.read()

Anti-Pattern 5: Checking Type with ==

# Bad
if type(value) == list:
    ...
# Good: respects inheritance
if isinstance(value, list):
    ...

Useful Standard Library Patterns

from itertools import chain, groupby, islice
from functools import lru_cache, partial
from pathlib import Path
import json

# Memoization
@lru_cache(maxsize=None)
def fib(n: int) -> int:
    return n if n < 2 else fib(n - 1) + fib(n - 2)

print(fib(50))   # 12586269025, instant thanks to caching

# Flatten nested lists
nested = [[1, 2], [3], [4, 5]]
print(list(chain.from_iterable(nested)))   # [1, 2, 3, 4, 5]

# Read and write JSON with pathlib
data = {"name": "Ada", "langs": ["Python"]}
Path("user.json").write_text(json.dumps(data, indent=2), encoding="utf-8")
loaded = json.loads(Path("user.json").read_text(encoding="utf-8"))

@lru_cache turns exponential recursion into linear time by storing results. pathlib.Path replaces most os.path string juggling with readable objects.

Async Basics

import asyncio

async def fetch(name: str, delay: float) -> str:
    await asyncio.sleep(delay)   # non-blocking wait
    return f"{name} done"

async def main() -> None:
    results = await asyncio.gather(
        fetch("A", 1.0),
        fetch("B", 1.0),
    )
    print(results)

asyncio.run(main())

Both tasks run concurrently, so the total runtime is about 1 second, not 2. Use asyncio for I/O-bound work such as HTTP calls and database queries. Use multiprocessing for CPU-bound work.

Quick Reference: Operators and Built-ins

Category Items
Arithmetic + - * / // % **
Comparison == != < > <= >=
Logical and or not
Identity / Membership is, is not, in, not in
Useful built-ins len(), sum(), min(), max(), any(), all(), sorted(), reversed(), map(), filter()

Note that / always returns a float, while // returns the floor-divided integer.

FAQ

What is the difference between a list and a tuple in Python?

A list is mutable, so you can add, remove, or change elements. A tuple is immutable and can serve as a dict key when its elements are hashable. Use tuples for fixed records like coordinates, and lists for collections that change.

When should I use a list comprehension instead of a for loop?

Use a comprehension when you build a new collection from one iterable with a simple transformation or filter. Switch to a regular for loop when the logic needs multiple statements, side effects, or nested conditions that hurt readability.

What is the difference between is and == in Python?

== compares values for equality. is compares object identity, meaning whether both names point to the same object in memory. Use is only for singletons like None, as in if x is None.

How do I avoid the mutable default argument bug?

Set the default to None and create the mutable object inside the function body. Default values are evaluated once at function definition time, so a default [] or {} is shared across every call.

Hot this week

Robotics Predictions for 2027

Seven data-driven robotics predictions for 2027: VLA policies, humanoid actuators, ROS 2 real-time, safety filters, and code. Read the engineering roadmap.

Robots and Jobs: What the Evidence Says in 2026

Robots and jobs in 2026: IFR installation data, wage and employment estimates, and a payback model engineers can run. Read the evidence.

Robotics Careers in 2026: Roles, Skills, and Salaries

Explore robotics careers in 2026: top roles, must-have ROS 2 and controls skills, and U.S. salary ranges. Build your robotics career roadmap today.

Robot Sensors Explained: How Robots Perceive the World

Learn how LiDAR, IMU, encoders, and depth cameras work, with fusion math and ROS2 code. Build a reliable perception stack today.

20 Beginner Robot Projects Ranked by Difficulty

Build 20 beginner robot projects ranked from easiest to hardest, with wiring, code, and ROS2 steps. Pick your first build and start today.

Topics

Robotics Predictions for 2027

Seven data-driven robotics predictions for 2027: VLA policies, humanoid actuators, ROS 2 real-time, safety filters, and code. Read the engineering roadmap.

Robots and Jobs: What the Evidence Says in 2026

Robots and jobs in 2026: IFR installation data, wage and employment estimates, and a payback model engineers can run. Read the evidence.

Robotics Careers in 2026: Roles, Skills, and Salaries

Explore robotics careers in 2026: top roles, must-have ROS 2 and controls skills, and U.S. salary ranges. Build your robotics career roadmap today.

Robot Sensors Explained: How Robots Perceive the World

Learn how LiDAR, IMU, encoders, and depth cameras work, with fusion math and ROS2 code. Build a reliable perception stack today.

20 Beginner Robot Projects Ranked by Difficulty

Build 20 beginner robot projects ranked from easiest to hardest, with wiring, code, and ROS2 steps. Pick your first build and start today.

Robot Computers in 2026: Raspberry Pi 5, Jetson, and Industrial PCs Compared

Raspberry Pi 5 vs NVIDIA Jetson vs industrial PCs for ROS 2 robots. See specs, latency math and a decision matrix, then pick your compute.

AI in Robotics: Programmed Behavior vs Machine Learning (and When You Need Each)

Programmed control or machine learning? Compare PID, FSMs, and RL with ROS2 code, benchmarks, and a decision framework. Pick the right one.

What Is ROS 2? A Beginner’s Guide to the Robot Operating System in 2026

Learn what ROS 2 is, how nodes, topics, and DDS work, and build your first publisher and subscriber. Start your ROS 2 project today.

Related Articles

Popular Categories