Skip to main content

Python cheatsheet

Learn Python step by step, from your first variable to running tasks side by side. Each section has three parts:

  • In short β€” the idea in one sentence.
  • Example β€” code with a comment on every line saying what it produces.
  • Try it β€” a tiny exercise to check you understood.

Want the longer story behind a topic? The complete guide walks through it in more depth.

πŸ“– Full guide: Python β†’ Beginner
How to use this page

Go through Part 1 in order β€” each section builds on the one before. Once you're comfortable, move to Part 2. Part 3 is for when you're writing larger programs. Type the examples yourself instead of copying them; you'll remember them far better.

Contents​

Part 1 β€” Beginner: Variables & values Β· Text & numbers Β· Making decisions & loops Β· Collections Β· Functions Β· Handling errors Β· Reading & writing files Β· Everyday one-liners

Part 2 β€” Core: Comprehensions Β· Extra collection types Β· Classes Β· Special methods Β· Modules & imports Β· Virtual environments Β· Testing Β· Logging Β· Text patterns (regex) Β· Common mistakes

Part 3 β€” Advanced: Scope & closures Β· Decorators Β· Generators & iterators Β· Type hints Β· Doing several things at once Β· Async basics Β· Memory & speed

Part 1 β€” Beginner​

1. Variables & values​

In short: a variable is a name that holds a value. The value's type says what kind of thing it is β€” text, a number, true/false, and so on.

name = "Ada"        # str   β€” text, always in quotes
age = 30 # int β€” whole number
price = 9.99 # float β€” decimal number
active = True # bool β€” True or False
missing = None # None β€” "no value yet"

print(name) # shows: Ada
type(age) # <class 'int'> β€” asks Python what type it is

Python groups code by indentation (four spaces), not { } braces:

if age >= 18:
print("adult") # indented, so it belongs to the if
print("done") # not indented, so it always runs

Comparing: use == to ask "same value?" and is None to check for None.

Try it: make a variable city holding your city's name, then print "I live in " followed by it.

2. Text & numbers​

In short: text (str) can be sliced and filled in; numbers (int, float) work like a calculator.

text = "Python"               # str
text[0] # "P" β€” first character (counting starts at 0)
text[1:4] # "yth" β€” characters 1, 2, 3
len(text) # 6 β€” how many characters
text.upper() # "PYTHON"
f"Hello, {text}" # "Hello, Python" β€” an f-string fills in values

7 + 2 * 3 # 13 β€” multiplication happens first
7 / 2 # 3.5 β€” normal division, always a float
7 // 2 # 3 β€” division that drops the decimal part
7 % 2 # 1 β€” the remainder
2 ** 3 # 8 β€” 2 to the power of 3

Try it: with name = "ada lovelace", print it with a capital first letter on each word. (Hint: .title().)

3. Making decisions & loops​

In short: if chooses what to run; for repeats something for each item; while repeats until a condition stops being true.

score = 85                    # int

if score >= 90:
grade = "A"
elif score >= 80: # "elif" means "else if"
grade = "B" # this one runs
else:
grade = "C"

names = ["Ada", "skip", "Linus"] # list of str
for name in names:
if name == "skip":
continue # skip to the next name
print(name) # prints Ada, then Linus

for number in range(3): # range(3) gives 0, 1, 2
print(number)

count = 3
while count > 0:
print(count) # 3, 2, 1
count -= 1 # same as count = count - 1
  • break β€” leave the loop completely.
  • continue β€” skip the rest of this round.
  • pass β€” a placeholder that does nothing.

Empty means false: 0, "", None, [], {} all count as false in an if. So if names: means "if the list isn't empty".

Try it: print the numbers 1 to 10, but print "Fizz" instead of any number divisible by 3.

4. Collections​

In short: a collection holds many values in one variable. Python has four built-in kinds, and their brackets tell them apart.

TypeLooks likeKeeps order?Can change?Allows duplicates?Use it for
list[1, 2, 2]YesYesYesA sequence of items
tuple(1, 2, 2)YesNoYesA fixed group, like (x, y)
set{1, 2}NoYesNoUnique items, fast "is it in?"
dict{"a": 1}YesYesKeys: noLooking up a value by a name
# list β€” an ordered, changeable sequence
items = ["a", "b"]
items.append("c") # ["a", "b", "c"]
items[0] # "a"
len(items) # 3

# tuple β€” like a list, but fixed once created
point = (10, 20)
x, y = point # "unpacking": x = 10, y = 20

# set β€” unique values only, no order
unique = {"a", "b", "a"} # {"a", "b"} β€” the duplicate is dropped
"a" in unique # True

# dict β€” pairs of key β†’ value
user = {"name": "Ada", "age": 30}
user["name"] # "Ada"
user["age"] = 31 # change a value
user.get("email", "unknown") # "unknown" β€” safe if the key is missing
for key, value in user.items():
print(key, value) # name Ada / age 31

Try it: make a dict of three friends and their ages, then print only the friends older than 25.

5. Functions​

In short: a function is a named, reusable block of code. You give it inputs (arguments) and it can give back a result with return.

def add(a, b=0):                  # b is optional, defaults to 0
return a + b

add(2) # 2
add(2, 3) # 5
add(a=2, b=3) # 5 β€” arguments can be passed by name

Type hints are optional labels saying what goes in and out. They help readers and editors, but Python doesn't enforce them:

def greet(name: str) -> str:      # takes text, returns text
return f"Hello, {name}"

Taking any number of arguments:

def show(*args, **kwargs):
print(args) # tuple of the unnamed values
print(kwargs) # dict of the named values

show(1, 2, colour="blue")
# args β†’ (1, 2) a tuple
# kwargs β†’ {"colour": "blue"} a dict

Watch out: never use a list or dict as a default value β€” Python reuses the same one on every call. Use None and create it inside:

def add_item(item, items=None):
if items is None:
items = [] # a fresh list each call
items.append(item)
return items

Try it: write average(numbers) that takes a list of numbers and returns their average. (Hint: sum() and len().)

6. Handling errors​

In short: when something goes wrong, Python raises an error. try / except lets you catch it and respond instead of crashing.

user_input = "abc"

try:
number = int(user_input) # fails: "abc" isn't a number
except ValueError:
print("Please type a number") # runs because it failed
else:
print("Got", number) # runs only if it worked
finally:
print("Finished") # always runs
  • Catch the specific error you expect (ValueError), not every error.
  • Never write a bare except: β€” it hides real bugs.
  • Raise your own error with raise ValueError("age can't be negative").

Try it: ask for a number with input(), and keep asking until the user types a valid one.

7. Reading & writing files​

In short: open a file inside a with block β€” Python closes it for you automatically when the block ends, even if an error happens.

# write text to a file
with open("notes.txt", "w", encoding="utf-8") as file:
file.write("Hello\nWorld\n")

# read it back
with open("notes.txt", encoding="utf-8") as file:
for line in file: # one line (str) at a time
print(line.strip()) # strip() removes the newline

# JSON: turn a dict into a file and back
import json
settings = {"theme": "dark"} # dict
with open("settings.json", "w", encoding="utf-8") as f:
json.dump(settings, f) # dict β†’ file
with open("settings.json", encoding="utf-8") as f:
loaded = json.load(f) # file β†’ dict

"w" means write (replaces the file), "a" means append, and no mode means read. Always pass encoding="utf-8" so files behave the same on every computer.

For CSV (spreadsheet-like) files, use csv.DictReader β€” it gives each row as a dict keyed by column name.

Try it: write three names to a file, one per line, then read the file and print how many names it has.

8. Everyday one-liners​

In short: small built-in tools you'll use constantly.

data = {"a": 1}                 # dict
names = ["Ada", "Linus"] # list of str
items = [3, 1, 2] # list of int

"a" in data # True β€” does the key exist?
data.get("z", 0) # 0 β€” lookup with a fallback
", ".join(names) # "Ada, Linus" β€” list β†’ one string
"Ada, Linus".split(", ") # ["Ada", "Linus"] β€” string β†’ list
sorted(items) # [1, 2, 3] β€” new sorted list
sorted(items, reverse=True) # [3, 2, 1]
min(items), max(items), sum(items) # (1, 3, 6)
items[::-1] # [2, 1, 3] β€” reversed copy
all([True, True, False]) # False β€” are ALL true?
any([True, False, False]) # True β€” is ANY true?
for i, name in enumerate(names):
print(i, name) # 0 Ada / 1 Linus β€” index plus item

Part 2 β€” Core​

9. Comprehensions​

In short: a one-line way to build a new collection from an existing one. The brackets decide which collection type you get.

numbers = [1, 2, 3, 4]                      # list

doubled = [n * 2 for n in numbers] # list: [2, 4, 6, 8]
evens = [n for n in numbers if n % 2 == 0] # list: [2, 4]
squares = {n: n * n for n in numbers} # dict: {1: 1, 2: 4, 3: 9, 4: 16}
unique = {n % 2 for n in numbers} # set: {0, 1}

Read it left to right as: "give me n * 2 for each n in numbers if it passes the test".

The same thing as a normal loop:

doubled = []
for n in numbers:
doubled.append(n * 2)

Try it: from words = ["apple", "kiwi", "banana"], build a list of only the words longer than 4 letters.

10. Extra collection types​

In short: the collections module has ready-made types for common chores that would take several lines with a plain list or dict.

from collections import Counter, defaultdict, deque

# Counter β€” a dict that counts things for you
counts = Counter("banana") # Counter({'a': 3, 'n': 2, 'b': 1})
counts.most_common(1) # [('a', 3)] β€” list of (item, count) tuples

# defaultdict β€” a dict that fills in a starting value for missing keys
groups = defaultdict(list) # missing keys start as an empty list
groups["fruit"].append("apple") # no error, even the first time
# {'fruit': ['apple']}

# deque ("deck") β€” a queue that's fast to add/remove at BOTH ends
queue = deque([1, 2, 3])
queue.appendleft(0) # deque([0, 1, 2, 3])
queue.popleft() # 0 β€” removed from the front
You need to…Use
Count how often each item appearsCounter
Group items into lists by a keydefaultdict(list)
Add at one end and remove from the otherdeque

Try it: use Counter to find the most common word in a sentence. (Hint: sentence.split().)

11. Classes​

In short: a class is a blueprint for making objects that bundle data and the functions (methods) that work on it.

class Dog:
def __init__(self, name): # runs when you create a Dog
self.name = name # store data on the object

def speak(self): # a method β€” self is the object itself
return f"{self.name} says woof"

rex = Dog("Rex") # create an object
rex.speak() # "Rex says woof"

A dataclass is a shortcut for classes that mainly hold data β€” Python writes __init__ and a readable printout for you:

from dataclasses import dataclass, field

@dataclass
class User:
name: str # str field
tags: list[str] = field(default_factory=list) # new empty list per user

user = User("Ada") # User(name='Ada', tags=[])

Inheritance β€” a class can build on another:

class Puppy(Dog):                 # a Puppy is a kind of Dog
def speak(self):
return super().speak() + " (tiny)" # super() calls Dog's version

Use inheritance only for a true "is a" relationship. If one thing just has another (a Car has an Engine), store it as an attribute instead.

Try it: make a BankAccount class with deposit(amount) and withdraw(amount) methods and a balance.

12. Special methods​

In short: methods named like __len__ (double underscore on both sides, nicknamed "dunder") let your objects work with Python's built-in features.

class Team:
def __init__(self, members):
self.members = members # list of str

def __len__(self): # makes len(team) work
return len(self.members)

def __iter__(self): # makes "for m in team" work
return iter(self.members)

team = Team(["Ada", "Linus"])
len(team) # 2
for member in team:
print(member) # Ada, Linus
You want this to work…Add this method
print(obj) shows something useful__str__
len(obj)__len__
for x in obj__iter__
obj1 == obj2__eq__
with obj:__enter__ and __exit__

13. Modules & imports​

In short: a module is just a .py file. import lets one file use code from another.

import math                           # use as math.sqrt(16)
from math import sqrt # use as sqrt(16)
from datetime import datetime as dt # rename on import

if __name__ == "__main__":
main() # runs only when this file is run directly,
# not when another file imports it

Import the specific names you need. Avoid from module import * β€” it makes it hard to tell where a name came from.

14. Virtual environments​

In short: a virtual environment is a private folder of installed libraries for one project, so projects don't clash with each other.

python -m venv .venv                     # create one in a folder called .venv
source .venv/bin/activate # turn it on (macOS/Linux)
python -m pip install requests # install a library into it
python -m pip freeze > requirements.txt # save the list of installed libraries
python -m pip install -r requirements.txt # install that list on another machine

pip is Python's package installer. Writing python -m pip makes sure you install into the same Python you're running.

15. Testing​

In short: a test is a small function that checks your code gives the right answer. pytest finds and runs every function whose name starts with test_.

# file: test_maths.py
import pytest

def square(n):
if n < 0:
raise ValueError("negative")
return n * n

def test_square():
assert square(3) == 9 # passes if this is True

def test_negative_fails():
with pytest.raises(ValueError): # passes only if ValueError is raised
square(-1)

# run the same test with several inputs β€” a list of (input, expected) tuples
@pytest.mark.parametrize("value, expected", [(2, 4), (3, 9)])
def test_many(value, expected):
assert square(value) == expected

Run all tests with pytest in the terminal.

  • Fixtures β€” reusable setup shared across tests.
  • Mocking (unittest.mock.patch) β€” swap a slow or external thing, like a web request, for a fake during a test.

Try it: write a test for the average function you made in section 5.

16. Logging​

In short: logging is like print, but with severity levels, and it can send messages to files or monitoring tools.

import logging

logging.basicConfig(level=logging.INFO) # show INFO and above
logger = logging.getLogger(__name__) # a logger named after this file

logger.info("Starting job")
logger.warning("Disk almost full")
logger.error("Failed: %s", "timeout") # %s is replaced by "timeout"

Levels, least to most serious: DEBUG β†’ INFO β†’ WARNING β†’ ERROR β†’ CRITICAL.

17. Text patterns (regex)​

In short: a regular expression ("regex") describes a text pattern, like "three digits, a dash, four digits", so you can find or check it.

import re

re.fullmatch(r"\d{3}-\d{4}", "555-1234") # a match β†’ the text fits the pattern

text = "Mail ada@site.com or bob@web.org"
re.findall(r"\w+@\w+\.\w+", text)
# list of str: ["ada@site.com", "bob@web.org"]

re.sub(r"\d", "#", "Room 42") # "Room ##" β€” replace every digit
SymbolMeans
\da digit 0–9
\wa letter, digit, or _
\sa space, tab, or newline
+one or more of the previous thing
{3}exactly 3 of the previous thing
^ / $start / end of the text

Put an r before the quotes (r"...") so backslashes are kept as-is.

18. Common mistakes​

Copying a list that contains lists:

import copy

original = [[1, 2]] # list containing a list
shallow = original.copy() # new outer list, SAME inner list
deep = copy.deepcopy(original) # new outer AND inner lists

original[0].append(3)
shallow # [[1, 2, 3]] β€” changed too!
deep # [[1, 2]] β€” untouched

Two names, one list:

a = [1, 2]
b = a # b is NOT a copy β€” both names point to the same list
b.append(3)
a # [1, 2, 3]

What can be a dict key or set item? Only values that can't change: str, int, tuple βœ… β€” not list, dict, or set ❌.

  • Use is None to check for None, and == to compare values.
  • Strings can't be changed in place β€” .upper() returns a new string.
  • A set has no order; don't rely on the order you see when printing it.

Part 3 β€” Advanced​

19. Scope & closures​

In short: scope is where a variable can be seen. A closure is an inner function that remembers variables from the function around it.

Python looks for a name in this order, stopping at the first match:

  1. Local β€” inside the current function
  2. Enclosing β€” inside an outer function wrapping it
  3. Global β€” at the top level of the file
  4. Built-in β€” Python's own names like len and print
def make_counter():
count = 0 # int, lives in the outer function

def next_value():
nonlocal count # "use the outer count, don't make a new one"
count += 1
return count

return next_value # hand back the inner function

counter = make_counter()
counter() # 1
counter() # 2 β€” it remembered count

20. Decorators​

In short: a decorator wraps a function to add extra behaviour (like logging or timing) without changing the function's own code. @name applies it.

import functools

def announce(function):
@functools.wraps(function) # keeps the original function's name
def wrapper(*args, **kwargs):
print("starting")
return function(*args, **kwargs)
return wrapper

@announce # same as: work = announce(work)
def work():
print("working")

work() # prints "starting", then "working"

You've already seen decorators in this page: @dataclass and @pytest.mark.parametrize.

21. Generators & iterators​

In short: a generator hands out values one at a time using yield, instead of building the whole list in memory first.

def count_up(limit):
for number in range(1, limit + 1):
yield number # pause here and give back one number

for number in count_up(3):
print(number) # 1, 2, 3

lazy = (n * 2 for n in range(1_000_000)) # ( ) β†’ generator, not a list

An iterator gives the next item each time you ask β€” a for loop does this for you behind the scenes:

letters = iter(["a", "b"])      # iterator over a list
next(letters) # "a"
next(letters) # "b"
next(letters) # error: StopIteration β€” nothing left

A generator can be looped over only once; a list can be looped over many times.

22. Type hints​

In short: type hints describe what types your code expects. Tools such as mypy or pyright read them and flag mistakes before you run anything.

def total(values: list[int | float]) -> float:   # list of ints or floats
return sum(values)

def find_email(user_id: int) -> str | None: # text, or None if not found
...

ages: dict[str, int] = {"Ada": 36} # dict of str β†’ int

"Any object with a name" β€” describe a shape with Protocol:

from typing import Protocol

class HasName(Protocol):
name: str

def label(thing: HasName) -> str:
return thing.name # works for any object that has .name

23. Doing several things at once​

In short: pick the tool based on what is making your program slow.

Your program is slow because it is…UseWhy
Doing heavy calculations (maths, image processing)multiprocessingUses several CPU cores at once
Waiting on files, databases, or web requeststhreadingOther threads work while one waits
Waiting on many network calls, using async librariesasyncioOne thread juggles many waiting tasks

Why not threads for heavy maths? Standard Python lets only one thread run Python code at any moment (a rule called the GIL, "global interpreter lock"). Threads still help while waiting, because waiting doesn't need the lock. Separate processes each have their own, so they truly run in parallel.

from concurrent.futures import ThreadPoolExecutor

urls = ["https://a.com", "https://b.com"] # list of str
with ThreadPoolExecutor() as pool:
pages = list(pool.map(download, urls)) # download both at once

24. Async basics​

In short: async code lets one program wait on many slow things (like web requests) at the same time, without extra threads.

import asyncio

async def fetch(): # "async def" makes a pausable function
await asyncio.sleep(1) # pause here; other tasks run meanwhile
return "done"

async def main():
results = await asyncio.gather(fetch(), fetch()) # run both together
print(results) # ["done", "done"] β€” after ~1s, not 2s

asyncio.run(main()) # start the async program

Don't call ordinary slow functions (like time.sleep or requests.get) inside async code β€” they freeze everything. Use async versions instead (asyncio.sleep, httpx.AsyncClient).

25. Memory & speed​

In short: write clear code first, then measure, then speed up only the slow part.

import sys

numbers = [n for n in range(1_000_000)] # list β€” all million stored now
lazy = (n for n in range(1_000_000)) # generator β€” makes each on demand

sys.getsizeof(numbers) # ~8,000,000 bytes
sys.getsizeof(lazy) # ~200 bytes

import timeit
timeit.timeit("sum(range(100))", number=100_000) # seconds taken
  • Checking "is X in here?" many times β†’ use a set or dict, not a list.
  • Going through data once β†’ use a generator.
  • Adding/removing at the front β†’ use deque.
  • Finding the slow part of a whole program β†’ run python -m cProfile script.py.

For role-specific examples and longer explanations, see the complete guide.