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 β BeginnerGo 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.
| Type | Looks like | Keeps order? | Can change? | Allows duplicates? | Use it for |
|---|---|---|---|---|---|
list | [1, 2, 2] | Yes | Yes | Yes | A sequence of items |
tuple | (1, 2, 2) | Yes | No | Yes | A fixed group, like (x, y) |
set | {1, 2} | No | Yes | No | Unique items, fast "is it in?" |
dict | {"a": 1} | Yes | Yes | Keys: no | Looking 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 appears | Counter |
| Group items into lists by a key | defaultdict(list) |
| Add at one end and remove from the other | deque |
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
| Symbol | Means |
|---|---|
\d | a digit 0β9 |
\w | a letter, digit, or _ |
\s | a 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 Noneto check forNone, and==to compare values. - Strings can't be changed in place β
.upper()returns a new string. - A
sethas 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:
- Local β inside the current function
- Enclosing β inside an outer function wrapping it
- Global β at the top level of the file
- Built-in β Python's own names like
lenandprint
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⦠| Use | Why |
|---|---|---|
| Doing heavy calculations (maths, image processing) | multiprocessing | Uses several CPU cores at once |
| Waiting on files, databases, or web requests | threading | Other threads work while one waits |
| Waiting on many network calls, using async libraries | asyncio | One 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
setordict, not alist. - 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.