Jump to section
Python Cheatsheet
A practical Python guide covering data structures, decorators, async, and type hints.
Basics: Types
Python figures out types automatically — you don't have to declare them. But you can add type hints for clarity and better editor help.
the five core types
Python
age = 30 # int — whole number
price = 9.99 # float — decimal
name = "Alice" # str — text
active = True # bool — True or False
empty = None # None — no value
# check the type
type(age) # <class 'int'>
type(name) # <class 'str'>
# check if it's a specific type
isinstance(age, int) # Truetype hints
Python
age: int = 30
name: str = "Alice"
# hint parameters and return value
def greet(name: str) -> str:
return f"Hello, {name}"
def add(a: int, b: int = 0) -> int:
return a + b
# value that could be string OR None
def find_user(id: int) -> str | None:
... # ... means "not implemented yet"Type hints are optional but strongly recommended — they make bugs easier to spot before running your code.
converting between types
Python
int("42") # 42
float("3.14") # 3.14
str(100) # "100"
# falsy → False, everything else → True
bool(0) # False
bool("") # False
bool("hello") # True
# between collections
list((1, 2, 3)) # [1, 2, 3]
tuple([1, 2, 3]) # (1, 2, 3)Python never converts types for you — if you need a number from a string, use `int()` or `float()` yourself.
Strings
Strings are text. Python has excellent string tools — and f-strings make it easy to embed variables directly inside text.
f-strings
Python
name = "Alice"
score = 95.678
f"Hello, {name}!"
# "Hello, Alice!"
f"Score: {score:.2f}"
# "Score: 95.68"
f"2 + 2 = {2 + 2}"
# "2 + 2 = 4"
f"{'hello'.upper()}"
# "HELLO" — any expression worksAlways use f-strings instead of joining strings with `+` — cleaner and much easier to read.
searching & checking
Python
s = " Hello, World! "
# transform
s.upper()
# " HELLO, WORLD! "
s.lower()
# " hello, world! "
s.strip()
# "Hello, World!" — removes spaces both ends
s.replace("World", "Python")
# " Hello, Python! "
# search
s.startswith(" Hello") # True
s.endswith("!") # True
s.find("World")
# 8 — position of first match
# returns -1 if not found
"World" in s # TrueString methods never change the original — they always return a new string.
split & join
Python
# split — break into a list
"Hello, World".split(", ")
# ["Hello", "World"]
"a,b,c".split(",")
# ["a", "b", "c"]
# join — combine a list into a string
", ".join(["a", "b", "c"])
# "a, b, c"
"-".join(["2024", "01", "15"])
# "2024-01-15"string slicing
Python
s = "Hello, World!"
s[0] # "H" — first character
s[-1] # "!" — last character
s[0:5] # "Hello"
s[7:] # "World!" — index 7 to end
s[:5] # "Hello" — start to index 5
# reverse the whole string
s[::-1] # "!dlroW ,olleH"Numbers & Math
Python handles whole numbers and decimals with no surprises. The math module gives you everything else.
basic operations
Python
10 + 3 # 13
10 - 3 # 7
10 * 3 # 30
10 / 3 # 3.333.. — always float
10 // 3 # 3 — integer division
10 % 3 # 1 — remainder
2 ** 10 # 1024 — power
abs(-5) # 5 — remove minus sign`/` always returns a float — even `10 / 2` gives `5.0`. Use `//` if you need a whole number.
rounding and formatting
Python
import math
round(3.14159, 2) # 3.14
math.floor(3.9) # 3 — always down
math.ceil(3.1) # 4 — always up
math.sqrt(16) # 4.0 — square root
# format as readable string
f"{3.14159:.2f}" # "3.14"
f"{1000000:,}" # "1,000,000"random numbers
Python
import random
random.random()
# decimal between 0.0 and 1.0
random.randint(1, 10)
# whole number from 1 to 10 (inclusive)
random.choice(["a", "b", "c"])
# pick one random item
random.shuffle([1, 2, 3, 4])
# shuffle in place — changes the originalLists
A list is an ordered collection of items. You can add, remove, search, and loop over them. Lists are Python's most used data structure.
create and access items
Python
fruits = ["apple", "banana", "cherry"]
fruits[0] # "apple" — first item
fruits[-1] # "cherry" — last item
fruits[1:3]
# ["banana", "cherry"] — slice
len(fruits) # 3
# create from a range
list(range(5))
# [0, 1, 2, 3, 4]
list(range(1, 10, 2))
# [1, 3, 5, 7, 9] — start, stop, stepadd and remove items
Python
a = [1, 2, 3]
a.append(4)
# [1, 2, 3, 4]
a.insert(1, 99)
# [1, 99, 2, 3, 4]
a.remove(99)
# [1, 2, 3, 4] — removes first match
a.pop()
# removes & returns last item
a.pop(0)
# removes & returns at index 0
a.extend([4, 5])
# add multiple items
a.sort() # sort in place
a.reverse() # reverse in place
a.clear() # remove everything → []useful built-in functions
Python
nums = [3, 1, 4, 1, 5, 9, 2]
min(nums) # 1 — smallest
max(nums) # 9 — largest
sum(nums) # 25 — total
# new sorted list — original unchanged
sorted(nums)
# [1, 1, 2, 3, 4, 5, 9]
# is at least one item > 8?
any(x > 8 for x in nums) # True
# are ALL items > 0?
all(x > 0 for x in nums) # True
nums.count(1) # 2 — how many times does 1 appear
nums.index(4) # 2 — index of first 4`sorted()` returns a new list and leaves the original unchanged. `list.sort()` changes the original in place.
Comprehensions & Generators
Comprehensions build collections in one line. Generators produce values one at a time instead of all at once — great for large data.
list comprehensions
Python
# [what_to_keep for item in collection if condition]
squares = [x**2 for x in range(5)]
# [0, 1, 4, 9, 16]
evens = [x for x in range(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]
upper = [s.upper() for s in ["a", "b"]]
# ["A", "B"]
# same thing without comprehension
squares = []
for x in range(5):
squares.append(x**2)all four comprehension types
Python
nums = range(10)
# list → [ ]
squares = [x**2 for x in nums]
# dict → { key: value }
sq_map = {x: x**2 for x in nums}
# set → { } — no duplicates
sq_set = {x**2 for x in nums}
# generator → ( ) — lazy, one value at a time
sq_gen = (x**2 for x in nums)
next(sq_gen) # 0 — get next value
list(sq_gen) # get all remaining
# all support conditions
evens = [x for x in nums if x % 2 == 0]generator functions
Python
def fibonacci():
a, b = 0, 1
while True:
yield a # pause and return a
a, b = b, a + b
fib = fibonacci()
next(fib) # 0
next(fib) # 1
next(fib) # 1
next(fib) # 2
# get the first 8 numbers
import itertools
list(itertools.islice(fibonacci(), 8))
# [0, 1, 1, 2, 3, 5, 8, 13]Tuples & Sets
Tuples are like lists but can never be changed. Sets are like lists but every value must be unique.
tuples — a list that can't change
Python
point = (3, 4)
point[0] # 3
# unpack into variables
x, y = point
# single-item — trailing comma required
single = (42,) # ✅ a tuple
not_a_tuple = (42) # ❌ just the number 42
# return multiple values from a function
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([3, 1, 4, 1, 5])
# low=1, high=5A single-item tuple needs a trailing comma: `(42,)` — without it, `(42)` is just the number 42.
sets — a collection with no duplicates
Python
# duplicates removed automatically
s = {1, 2, 3, 2, 1}
# {1, 2, 3}
s.add(4)
s.remove(2) # error if not found
s.discard(99) # no error if missing
99 in s # False — fast check
# set operations
a = {1, 2, 3}
b = {2, 3, 4}
a | b # {1, 2, 3, 4} — union
a & b # {2, 3} — intersection
a - b # {1} — difference
# remove duplicates from a list
unique = list(set([1, 2, 2, 3, 3]))
# [1, 2, 3]Most common use: remove duplicates from a list with `list(set(your_list))`.
Dictionaries
A dictionary stores data as key-value pairs — like a real dictionary where you look up a word (key) to find its meaning (value).
create and access
Python
user = {
"name": "Alice",
"age": 30,
"active": True,
}
user["name"]
# "Alice"
user.get("email")
# None — no error if missing
user.get("email", "no email")
# "no email" — fallback value
"name" in user
# True — check if key exists
# add or update
user["email"] = "[email protected]"
user.update({"age": 31, "role": "admin"})
# update multiple at onceloop over a dictionary
Python
scores = {"Alice": 95, "Bob": 87, "Carol": 91}
# keys only
for name in scores:
print(name)
# key + value (most common)
for name, score in scores.items():
print(f"{name}: {score}")
# get as lists
scores.keys()
# dict_keys(["Alice", "Bob", "Carol"])
scores.values()
# dict_values([95, 87, 91])
scores.items()
# dict_items([("Alice", 95), ...])dict comprehensions
Python
# map each number to its square
squares = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# filter — keep only scores above 90
scores = {"Alice": 95, "Bob": 87, "Carol": 91}
top = {
name: s
for name, s in scores.items()
if s > 90
}
# {"Alice": 95, "Carol": 91}merging two dictionaries
Python
defaults = {"theme": "light", "lang": "en"}
custom = {"lang": "fr", "font": "mono"}
# Python 3.9+ — cleanest
merged = defaults | custom
# {"theme": "light", "lang": "fr", "font": "mono"}
# Python 3.5+ — also works
merged = {**defaults, **custom}
# same resultWhen merging, keys on the right overwrite keys on the left. Use `|` (Python 3.9+) — it's the cleanest syntax.
Control Flow
Python uses indentation to define blocks — no curly braces. Every if, for, and while block ends with a colon :.
if / elif / else
Python
score = 85
if score >= 90:
grade = "A"
elif score >= 80: # 'elif' not 'else if'
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
# one-line ternary
status = "pass" if score >= 60 else "fail"for loops
Python
# loop over a list
for fruit in ["apple", "banana", "cherry"]:
print(fruit)
# loop with index — enumerate()
for i, fruit in enumerate(["apple", "banana"]):
print(i, fruit)
# 0 apple
# 1 banana
# loop a set number of times
for i in range(5):
print(i)
# 0, 1, 2, 3, 4
# loop two lists together — zip()
names = ["Alice", "Bob"]
scores = [95, 87]
for name, score in zip(names, scores):
print(f"{name}: {score}")while loops
Python
n = 0
while n < 5:
n += 1
if n == 3:
continue # skip this iteration
if n == 5:
break # stop the loop
print(n)
# prints 1, 2, 4pass, break and continue
Python
# pass — empty block placeholder
def todo():
pass # ✅ no error — just a placeholder
class Empty:
pass # ✅ empty class
if True:
pass # ✅ empty if block
# break — exit immediately
for i in range(10):
if i == 5:
break
# stops at 4
# continue — skip to next
for i in range(5):
if i == 2:
continue
# prints 0, 1, 3, 4match / case
Python
command = "quit"
match command:
case "quit":
print("Quitting...")
case "help":
print("Showing help...")
case _:
# _ is the default (like 'else')
print(f"Unknown: {command}")
# match on shapes too
point = (1, 0)
match point:
case (0, 0): print("Origin")
case (x, 0): print(f"x-axis at {x}")
case (0, y): print(f"y-axis at {y}")
case (x, y): print(f"Point at {x}, {y}")Functions
A function is a reusable block of code. Define it once with def, then call it as many times as you need.
function basics
Python
def greet(name: str) -> str:
return f"Hello, {name}!"
greet("Alice")
# "Hello, Alice!"
# default parameter
def power(base: float, exp: int = 2) -> float:
return base ** exp
power(3) # 9 — exp defaults to 2
power(2, 10) # 1024*args and **kwargs
Python
# *nums → collects all args into a tuple
def total(*nums: float) -> float:
return sum(nums)
total(1, 2, 3) # 6
total(10, 20, 30, 40) # 100
# **info → collects keyword args into a dict
def show_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
show_info(name="Alice", age=30)
# name: Alice
# age: 30lambda
Python
# lambda arguments: expression
double = lambda x: x * 2
double(5) # 10
# most common use — sort by a specific field
people = [
{"name": "Bob", "age": 25},
{"name": "Alice", "age": 30},
]
people.sort(key=lambda p: p["age"])
# sorted by ageclosures
Python
def multiplier(factor):
# 'factor' is remembered by the inner function
def multiply(n):
return n * factor
return multiply
double = multiplier(2)
triple = multiplier(3)
double(5) # 10
triple(5) # 15decorators
Python
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}...")
result = func(*args, **kwargs)
print("Done.")
return result
return wrapper
# same as: add = log(add)
@log
def add(a, b):
return a + b
add(2, 3)
# Calling add...
# Done.
# returns 5Classes & OOP
A class is a blueprint for creating objects. Python uses self to refer to the object itself — it's always the first parameter of every method.
basic class
Python
class Animal:
def __init__(self, name: str, species: str):
self.name = name
self.species = species
def speak(self) -> str:
return f"{self.name} makes a sound."
def __str__(self) -> str:
# what print() shows
return self.name
cat = Animal("Mia", "Cat")
cat.speak() # "Mia makes a sound."
print(cat) # "Mia"inheritance
Python
class Dog(Animal):
def __init__(self, name: str, breed: str):
super().__init__(name, "Dog")
self.breed = breed
def speak(self) -> str:
# override parent method
return f"{self.name} barks!"
@classmethod
def from_shelter(cls, name: str) -> "Dog":
# called on the class, not an instance
return cls(name, "Mixed")
@staticmethod
def is_domestic() -> bool:
# doesn't need class or instance
return True
rex = Dog("Rex", "Labrador")
rex.speak() # "Rex barks!"
Dog.is_domestic() # True
Dog.from_shelter("Buddy") # Dog(name="Buddy", breed="Mixed")dataclasses
Python
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float
z: float = 0.0 # optional with default
@dataclass
class Config:
host: str = "localhost"
port: int = 8080
tags: list[str] = field(default_factory=list)
# mutable defaults need field()
p = Point(1.0, 2.0)
print(p)
# Point(x=1.0, y=2.0, z=0.0) — auto __repr__
p1 = Point(1, 2)
p2 = Point(1, 2)
p1 == p2
# True — auto __eq__ compares all fieldsproperties
Python
class Temperature:
def __init__(self, celsius: float):
self._celsius = celsius
# _ means "private, don't touch directly"
@property
def celsius(self) -> float:
return self._celsius
# runs when you read .celsius
@celsius.setter
def celsius(self, value: float):
if value < -273.15:
raise ValueError("Below absolute zero!")
self._celsius = value
# runs when you write .celsius = something
@property
def fahrenheit(self) -> float:
# computed from celsius
return self._celsius * 9/5 + 32
t = Temperature(100)
t.fahrenheit # 212.0
t.celsius = 0 # validation runs
t.fahrenheit # 32.0Error Handling
When something goes wrong, Python raises an exception. Use try/except to handle it gracefully instead of crashing.
try / except / else / finally
Python
try:
number = int(input("Enter a number: "))
result = 10 / number
except ValueError:
# user typed text, not a number
print("That's not a valid number.")
except ZeroDivisionError:
print("You can't divide by zero.")
except Exception as e:
# catch-all for anything unexpected
print(f"Something went wrong: {e}")
else:
# runs ONLY if no exception occurred
print(f"Result: {result}")
finally:
# ALWAYS runs
print("Done.")custom exceptions
Python
class ValidationError(Exception):
def __init__(self, field: str, message: str):
super().__init__(message)
self.field = field
try:
raise ValidationError(
"email",
"Email format is invalid"
)
except ValidationError as e:
print(f'Problem with "{e.field}": {e}')
# Problem with "email": Email format is invalidwith statement — automatic cleanup
Python
# file closes automatically — even if error occurs
with open("data.txt", "r") as f:
content = f.read()
# file is closed here
# custom context manager
from contextlib import contextmanager
@contextmanager
def timer():
import time
start = time.time()
yield # code inside 'with' runs here
print(f"Took {time.time() - start:.2f}s")
with timer():
do_something_slow()
# prints how long it tookFile I/O
Python makes reading and writing files simple. Always use with open() — it guarantees the file closes properly even if something goes wrong.
reading and writing text files
Python
# read whole file
with open("notes.txt", "r", encoding="utf-8") as f:
content = f.read()
# read line by line
with open("notes.txt") as f:
for line in f:
print(line.strip())
# .strip() removes the newline
# write — creates or OVERWRITES
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello\n")
f.writelines(["line1\n", "line2\n"])
# append — adds to the end
with open("log.txt", "a") as f:
f.write("New log entry\n")'w' mode overwrites the entire file — use 'a' if you want to keep existing content.
JSON files
Python
import json
data = {"name": "Alice", "scores": [95, 87, 92]}
# save to file
with open("data.json", "w") as f:
json.dump(data, f, indent=2)
# indent=2 makes it human-readable
# load from file
with open("data.json") as f:
loaded = json.load(f)
# without a file
text = json.dumps(data)
# dict → JSON string
parsed = json.loads('{"x": 1}')
# JSON string → dictpathlib — modern file paths
Python
from pathlib import Path
p = Path("data/notes.txt")
p.exists() # True / False
p.is_file() # True
p.suffix # ".txt"
p.stem # "notes"
p.parent # Path("data")
p.name # "notes.txt"
# join paths safely — works everywhere
base = Path("project")
config = base / "config" / "settings.json"
# read and write directly
config.read_text(encoding="utf-8")
config.write_text("content", encoding="utf-8")
# list all Python files
for f in Path(".").glob("*.py"):
print(f)Async & Await
Async lets your program do other things while waiting for slow operations — like network requests — without freezing or needing multiple threads.
async / await basics
Python
import asyncio
async def fetch_data(url: str) -> str:
await asyncio.sleep(1)
# simulate waiting for a server
return f"Data from {url}"
async def main():
result = await fetch_data("https://api.example.com")
print(result)
asyncio.run(main())run multiple tasks at the same time
Python
import asyncio
async def task(name: str, delay: float):
await asyncio.sleep(delay)
return f"{name} finished"
async def main():
# ❌ sequential — 1.0 + 0.5 + 0.8 = 2.3s total
# r1 = await task("A", 1.0)
# r2 = await task("B", 0.5)
# ✅ concurrent — all start at once, total = 1.0s
results = await asyncio.gather(
task("A", 1.0),
task("B", 0.5),
task("C", 0.8),
)
print(results)
# ["A finished", "B finished", "C finished"]
asyncio.run(main())Use `asyncio.gather()` whenever you have multiple independent async tasks — it runs them all at the same time.
aiohttp — async HTTP requests
Python
import aiohttp
import asyncio
async def get_json(url: str) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
response.raise_for_status()
# raises error for 4xx/5xx
return await response.json()
async def main():
data = await get_json(
"https://jsonplaceholder.typicode.com/users/1"
)
print(data["name"])
asyncio.run(main())Modules & Packages
Every .py file is a module you can import. Python comes with a huge standard library — always check it before installing a package.
importing
Python
import math
from math import sqrt, pi
from math import sqrt as square_root
import os.path
math.sqrt(16) # 4.0
sqrt(16) # 4.0
square_root(16) # 4.0Never use `from module import *` — it pollutes your namespace and makes it hard to know where names come from.
the __name__ == '__main__' guard
Python
# mymodule.py
def greet(name: str) -> str:
return f"Hello, {name}!"
# only runs when: python mymodule.py
# does NOT run when: import mymodule
if __name__ == "__main__":
print(greet("World"))
# Hello, World!collections module
Python
from collections import Counter, defaultdict, deque
# Counter — count item occurrences
words = ["apple", "banana", "apple", "cherry", "apple"]
Counter(words)
# Counter({'apple': 3, 'banana': 1, 'cherry': 1})
# defaultdict — no KeyError for missing keys
dd = defaultdict(list)
dd["fruits"].append("apple")
# missing key gets an empty list automatically
# deque — add/remove from front in O(1)
dq = deque([1, 2, 3])
dq.appendleft(0)
# [0, 1, 2, 3]datetime & re modules
Python
from datetime import datetime, timedelta
now = datetime.now()
later = now + timedelta(days=7)
now.strftime("%Y-%m-%d")
# "2024-01-15"
now.strftime("%d/%m/%Y %H:%M")
# "15/01/2024 14:30"
# re — regular expressions
import re
match = re.search(r"\d{4}-\d{2}-\d{2}", "Today is 2024-01-15")
if match:
print(match.group())
# "2024-01-15"
re.findall(r"\d+", "I have 3 cats and 12 dogs")
# ["3", "12"]
re.sub(r"\s+", "-", "hello world")
# "hello-world"Testing
pytest is the standard way to test Python code. Write functions that start with test_ and use assert to check that things work correctly.
writing tests
Python
# test_math.py
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5
def test_add_negative():
assert add(-1, 1) == 0
def test_raises():
import pytest
with pytest.raises(ZeroDivisionError):
1 / 0
# run tests
# pytest — all tests
# pytest test_math.py — one file
# pytest -v — verbose
# pytest -k "test_add" — by namefixtures — reusable test setup
Python
import pytest
@pytest.fixture
def user():
return {"name": "Alice", "age": 30, "active": True}
# receive fixture by name as a parameter
def test_user_name(user):
assert user["name"] == "Alice"
def test_user_age(user):
assert user["age"] == 30
def test_user_is_active(user):
assert user["active"] is Trueparametrize — run one test with many inputs
Python
import pytest
@pytest.mark.parametrize("a, b, expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
(10, -5, 5),
])
def test_add(a, b, expected):
assert a + b == expected
# pytest runs this 4 times — once per rowType Hints & mypy
Type hints are optional labels that tell you and your tools what types a function expects and returns. mypy checks them without running your code.
modern type syntax (Python 3.10+)
Python
# before 3.10 — required imports
from typing import Union, Optional
def old(value: Union[int, str]) -> Optional[str]: ...
# Python 3.10+ — clean and simple
def process(value: int | str) -> str:
return str(value)
def find(id: int) -> str | None:
...
# Python 3.9+ — built-in generics
def first(items: list[int]) -> int | None:
return items[0] if items else None
def lookup(data: dict[str, int], key: str) -> int:
return data[key]If you're on Python 3.10+, use the `|` syntax — it's shorter and doesn't need any imports.
generics
Python
from typing import TypeVar, Generic
T = TypeVar("T")
# returns the same type it receives
def identity(value: T) -> T:
return value
identity("hello") # returns str
identity(42) # returns int
# typed class that works for any type
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
s: Stack[int] = Stack()
s.push(1)
s.pop() # returns intTypedDict — typed dictionary shapes
Python
from typing import TypedDict, NotRequired
class User(TypedDict):
id: int
name: str
email: NotRequired[str]
# optional — can be missing
# mypy checks this matches User's shape
user: User = {"id": 1, "name": "Alice"}
# ✅ email is optional
user2: User = {
"id": 2,
"name": "Bob",
"email": "b@b.com"
}
# ✅ with email
# user3: User = {"id": 3}
# ❌ missing required 'name'Protocols — describe a shape without inheritance
Python
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Circle:
def draw(self) -> None:
print("Drawing a circle")
class Square:
def draw(self) -> None:
print("Drawing a square")
# both work — no need to inherit from Drawable
def render(shape: Drawable) -> None:
shape.draw()
render(Circle()) # ✅
render(Square()) # ✅mypy — check types without running your code
bash
pip install mypy
# check a single file
mypy main.py
# check entire folder
mypy src/
# strict mode — catches even more
mypy --strict main.py
# mypy.ini (project root)
# [mypy]
# strict = True
# ignore_missing_imports = TrueNo login required to share feedback
More Cheatsheets
Keep your reference handy
Explore more zero-to-hero cheatsheets for the tools you use daily.