Skip to content

Cheat sheet · Python

Python Cheat Sheet

A scannable Python 3 reference covering variables, f-strings, string methods, lists, dicts, sets, tuples, comprehensions, control flow, functions, built-ins, exceptions, and file handling.

A cheat sheet is for the thing you have understood once and cannot quite remember the shape of. It is written to be scanned, so the common cases come first. Starting from nothing? The Python exercises are the right first step; come back here once the syntax is something you are recalling rather than meeting. Looking for another language? See every cheat sheet.

This page is a fast-scanning reference for Python 3 syntax you'll reach for constantly — not a tutorial. Each section is a self-contained group of runnable snippets, so jump straight to the part you need. Where a line's output isn't obvious from reading it, the comment after it (or a text block underneath) shows exactly what Python produces.

Variables & Types

Python variables don't need a declared type — the type lives with the value, not the variable name, and it can change if you reassign the variable to something else.

Python
name = "Priya"        # str
age = 27               # int
gpa = 3.85               # float
is_active = True          # bool
data = None                 # NoneType — represents "no value"

type(age)                    # <class 'int'>
isinstance(age, int)           # True

Converting between types is done with int(), float(), str(), and bool(). Note that int() on a float truncates toward zero — it does not round:

Python
int("42")            # 42
int(3.9)                # 3   — truncated, not rounded
float("2.5")               # 2.5
str(17)                       # '17'
bool(0)                          # False
bool("")                            # False
bool("False")                          # True — any non-empty string is truthy

Python lets you assign several variables at once, which is handy for swapping values or unpacking a known-length sequence:

Python
x, y = 1, 2
x, y = y, x                       # swap without a temp variable — x=2, y=1
a = b = c = 0                       # all three names point at 0
first, *rest = [1, 2, 3, 4]           # first=1, rest=[2, 3, 4]

String Formatting with f-strings

An f-string is a string literal prefixed with f. Anything inside {} is evaluated as a real Python expression and inserted into the string — this is the standard way to build strings in modern Python.

Python
name = "Priya"
score = 87.4567

f"Hello, {name}!"              # 'Hello, Priya!'
f"Score: {score:.1f}"            # 'Score: 87.5'   — 1 decimal place
f"Score: {score:.2f}%"             # 'Score: 87.46%' — 2 decimal places
f"{name!r}"                          # "'Priya'"       — !r inserts repr() instead of str()
f"{1_000_000:,}"                       # '1,000,000'     — thousands separator
f"{7:05d}"                                # '00007'         — zero-padded to width 5
f"{score:>10.2f}"                            # '     87.46'    — right-aligned in a width-10 field
f"{score:<10.2f}|"                             # '87.46     |'   — left-aligned, padding visible before |
f"{2 ** 10 = }"                                   # '2 ** 10 = 1024' — self-documenting expression

The = form in the last line (added in Python 3.8) is a debugging shortcut: it prints the literal expression text alongside its value, so you don't have to type the variable name twice. You can also call functions and use conditionals directly inside the braces:

Python
items = ["pen", "book"]
f"You have {len(items)} item{'s' if len(items) != 1 else ''}"   # 'You have 2 items'

Common String Methods

Strings are immutable — every method below returns a new string rather than changing the original in place.

Python
s = "  Hello, World!  "

s.strip()                                # 'Hello, World!'   — removes leading/trailing whitespace
s.lower()                                  # '  hello, world!  '
s.upper()                                    # '  HELLO, WORLD!  '
s.strip().replace("World", "Python")           # 'Hello, Python!'
s.strip().split(",")                             # ['Hello', ' World!']
"-".join(["a", "b", "c"])                          # 'a-b-c'   — opposite of split()
"Hello".startswith("He")                             # True
"Hello".endswith("lo")                                 # True
"hello world".title()                                    # 'Hello World'
"42".isdigit()                                              # True
"".isdigit()                                                   # False — empty string has no digits

find() and index() both search for a substring, but they disagree about what to do when nothing is found: find() returns -1, while index() raises a ValueError. Prefer find() when a missing match is a normal outcome, and index() when it should be treated as a bug:

Python
"Hello".find("l")      # 2  — index of the first match
"Hello".find("z")        # -1 — not found, no exception
"Hello".count("l")         # 2  — how many times "l" appears

Lists

A list is an ordered, mutable collection — you can change, add to, or remove from it after creation, and it can hold a mix of types.

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

fruits.append("date")             # add to the end
fruits.insert(1, "kiwi")            # insert at a specific index, shifting the rest right
fruits.remove("banana")               # delete the first item that equals "banana"
fruits.pop()                            # remove and return the last item
fruits.pop(0)                             # remove and return the item at index 0
fruits.extend(["fig", "grape"])             # append every item from another iterable
fruits.sort()                                 # sort in place, ascending
fruits.sort(reverse=True)                       # sort in place, descending
fruits.reverse()                                  # reverse order in place
fruits.count("kiwi")                                # how many times "kiwi" appears
fruits.index("fig")                                   # index of the first "fig"
"kiwi" in fruits                                        # membership test — True/False
len(fruits)                                               # number of items

append() adds one item (even if that item is itself a list); extend() adds every item from an iterable individually. Mixing them up is a common bug:

Python
a = [1, 2]
a.append([3, 4])     # [1, 2, [3, 4]]  — one new item, a nested list
b = [1, 2]
b.extend([3, 4])       # [1, 2, 3, 4]    — two new items

Slicing

list[start:stop:step] pulls out a sub-list. start is included, stop is excluded, and step defaults to 1. Any of the three can be left out.

Python
nums = [10, 20, 30, 40, 50]

nums[1:3]       # [20, 30]
nums[:2]           # [10, 20]         — from the beginning
nums[-2:]             # [40, 50]         — negative indices count from the end
nums[::-1]               # [50, 40, 30, 20, 10] — reversed copy
nums[::2]                   # [10, 30, 50]     — every other item

A slice always builds a brand-new list, so nums[:] is a quick way to copy a list without importing anything.

List Comprehensions

A comprehension builds a new list from an existing iterable in a single readable line — it's shorthand for a for loop that appends to an empty list.

Python
squares = [n ** 2 for n in range(6)]                      # [0, 1, 4, 9, 16, 25]
evens = [n for n in range(10) if n % 2 == 0]                 # [0, 2, 4, 6, 8]
labels = ["even" if n % 2 == 0 else "odd" for n in range(4)]   # ['even', 'odd', 'even', 'odd']
flat = [x for row in [[1, 2], [3, 4]] for x in row]               # [1, 2, 3, 4] — flattens one level

The if at the end of a comprehension filters which items are included; an if/else placed right after the expression instead chooses a value for every item, the way you saw in labels above — the two look similar but do very different jobs.

Dictionaries

A dictionary maps keys to values. Since Python 3.7, a dict remembers the order keys were inserted in.

Python
person = {"name": "Zoe", "age": 30}

person["age"]                    # 30           — raises KeyError if "age" were missing
person.get("age")                  # 30           — same lookup, but safe
person.get("email")                  # None         — missing key, no error, no fallback given
person.get("email", "n/a")             # 'n/a'        — missing key, with a fallback
person["email"] = "zoe@example.com"      # adds a new key (or overwrites an existing one)
person.pop("age")                          # 30 — removes "age" and returns its value
"name" in person                             # True — checks the keys, not the values

Square-bracket lookup (person["age"]) raises a KeyError for a missing key; .get() never raises — it hands back None, or whatever fallback you pass as the second argument. Reach for .get() whenever a missing key is a normal, expected possibility.

Python
config = {"debug": False}
config.update({"debug": True, "retries": 3})   # merges keys in place, overwriting duplicates
print(config)
Output
{'debug': True, 'retries': 3}

To combine two dicts into a brand-new one without mutating either, use ** unpacking or the | merge operator (Python 3.9+) — later dicts win on duplicate keys:

Python
defaults = {"theme": "dark", "retries": 3}
overrides = {"retries": 5, "verbose": True}

{**defaults, **overrides}       # {'theme': 'dark', 'retries': 5, 'verbose': True}
defaults | overrides              # same result, more readable

setdefault(key, default) is a one-line way to fetch a key, inserting default first if it's missing — useful for building up counts or groups without checking in first:

Python
counts = {}
counts.setdefault("a", 0)
counts["a"] += 1
print(counts)
Output
{'a': 1}

Dictionary Comprehensions

Python
nums = [1, 2, 3, 4]

{n: n ** 2 for n in nums}                              # {1: 1, 2: 4, 3: 9, 4: 16}
{n: n ** 2 for n in nums if n % 2 == 0}                  # {2: 4, 4: 16}
{v: k for k, v in {"a": 1, "b": 2}.items()}                # {1: 'a', 2: 'b'} — swap keys and values

Sets

A set is an unordered collection of unique, hashable values — duplicates are silently dropped, and sets are built for fast membership tests and the classic set-algebra operations.

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

a | b          # {1, 2, 3, 4, 5} — union: everything in either set
a & b            # {3}             — intersection: only what's in both
a - b              # {1, 2}          — difference: in a, but not in b
a ^ b                # {1, 2, 4, 5}    — symmetric difference: in exactly one set
a.add(10)              # adds a single value
a.discard(100)           # removes a value if present — no error if it's missing
3 in a                     # True

set(some_list) is a common idiom for deduplicating a list — the result loses the original order, so sort it afterward if order matters:

Python
sorted(set([3, 1, 2, 3, 1]))   # [1, 2, 3]

Sets also support comprehensions, using the same {} syntax as dict comprehensions but with a single expression instead of a key: value pair:

Python
{c.upper() for c in "banana"}   # {'B', 'A', 'N'} — unique letters only

Tuples

A tuple is an ordered collection like a list, but immutable — once created, you can't add, remove, or reassign its items.

Python
point = (3, 4)

x, y = point           # unpacking — x=3, y=4
point[0]                  # 3
len(point)                   # 2
point.count(3)                  # 1 — occurrences of the value 3

A single-item tuple needs a trailing comma, because parentheses alone don't make something a tuple:

Python
single = (5,)          # a one-item tuple
not_a_tuple = (5)        # just the int 5, in parentheses

Trying to change a tuple in place raises TypeError, which is exactly the point — it's a signal that this data shouldn't change:

Python
point = (3, 4)
point[0] = 10
Output
TypeError: 'tuple' object does not support item assignment

Because tuples are immutable (and therefore hashable, as long as every item inside them is too), they can be used as dictionary keys or stored in a set — lists never can:

Python
distances = {(0, 0): 0, (1, 1): 1.41}
distances[(0, 0)]      # 0

Reach for a list when the contents need to change over time, and a tuple for a fixed, related group of values — like coordinates, or a database row.

Control Flow

if / elif / else

Python has no switch statement — a chain of if/elif/else covers the same ground, and only the first matching branch runs.

Python
age = 20

if age < 13:
    stage = "child"
elif age < 20:
    stage = "teen"
else:
    stage = "adult"

print(stage)
Output
adult

For a simple choice between two values, a conditional expression (Python's version of a ternary operator) fits on one line:

Python
n = 7
label = "even" if n % 2 == 0 else "odd"   # 'odd'

for Loops

A for loop walks through any iterable — a list, string, dict, range(), and so on — handing you each item directly.

Python
for fruit in ["apple", "banana", "cherry"]:
    print(fruit)
Output
apple
banana
cherry

range(start, stop, step) generates numbers without building a list in memory, which is why it's the default choice for "do this N times" or "loop by index":

Python
list(range(5))            # [0, 1, 2, 3, 4]     — stop only: 0 up to (not including) 5
list(range(2, 10, 2))        # [2, 4, 6, 8]         — start, stop, step
list(range(5, 0, -1))           # [5, 4, 3, 2, 1]      — counting down

while Loops

A while loop keeps running as long as its condition stays true — use it when you don't know the number of iterations in advance.

Python
total = 0
n = 1
while n <= 5:
    total += n
    n += 1

print(total)
Output
15

break, continue, and for...else

break exits a loop immediately; continue skips straight to the next iteration without finishing the current one.

Python
for n in range(10):
    if n == 3:
        continue
    if n == 6:
        break
    print(n)
Output
0
1
2
4
5

A for loop can also carry an else clause, which runs only if the loop finished without hitting a break — a clean way to express "search and report if nothing was found":

Python
def has_factor(n, candidates):
    for c in candidates:
        if n % c == 0:
            print(f"{n} is divisible by {c}")
            break
    else:
        print(f"{n} has no factor in {candidates}")

has_factor(17, [2, 3, 5])
has_factor(15, [2, 3, 5])
Output
17 has no factor in [2, 3, 5]
15 is divisible by 3

Functions

def defines a function; parameters after = are optional and use the given default when the caller omits them.

Python
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Sam"))
print(greet("Sam", "Hey"))
Output
Hello, Sam!
Hey, Sam!

Watch out for mutable default arguments. A default value is created exactly once, when the function is defined — not on every call. If that default is a mutable object like a list, every call that relies on the default shares the same object:

Python
def add_item(item, basket=[]):
    basket.append(item)
    return basket

print(add_item("apple"))
print(add_item("banana"))
Output
['apple']
['apple', 'banana']

The second call's basket already contains "apple", because both calls reused the one list created when add_item was defined. The standard fix is to default to None and create a fresh list inside the function body:

Python
def add_item_safe(item, basket=None):
    if basket is None:
        basket = []
    basket.append(item)
    return basket

*args and **kwargs

*args collects any extra positional arguments into a tuple; **kwargs collects any extra keyword arguments into a dict. Use them when a function needs to accept a variable number of inputs.

Python
def total(*numbers, **labels):
    print("numbers:", numbers)
    print("labels:", labels)
    return sum(numbers)

result = total(1, 2, 3, unit="kg", source="scale")
print(result)
Output
numbers: (1, 2, 3)
labels: {'unit': 'kg', 'source': 'scale'}
6

A bare * in a parameter list (with no name after it) doesn't collect anything itself — it just forces every parameter after it to be passed by keyword only, which is a good habit for arguments where the order isn't obvious:

Python
def connect(host, *, port=443, secure=True):
    return f"{host}:{port} (secure={secure})"

print(connect("example.com", port=8080))
Output
example.com:8080 (secure=True)

Multiple Return Values

Returning several values separated by commas actually returns one tuple, which you can unpack right back into separate names at the call site.

Python
def min_max(values):
    return min(values), max(values)

lowest, highest = min_max([4, 1, 7, 3])
print(lowest, highest)
Output
1 7

Lambda Functions

lambda creates a small, unnamed function in a single expression — no def, no return keyword, and no statements, just one expression whose value is returned automatically. They're most useful as a short throwaway function passed to something like sorted() or map().

Python
square = lambda x: x * x
print(square(5))
Output
25

Useful Built-in Functions

| Function | Example | Result | |---|---|---| | len() | len([1, 2, 3]) | 3 | | range() | list(range(3)) | [0, 1, 2] | | enumerate() | list(enumerate(["a", "b"])) | [(0, 'a'), (1, 'b')] | | zip() | list(zip([1, 2], ["a", "b"])) | [(1, 'a'), (2, 'b')] | | sorted() | sorted([3, 1, 2]) | [1, 2, 3] | | map() | list(map(str, [1, 2, 3])) | ['1', '2', '3'] | | filter() | list(filter(None, [0, 1, 2, ""])) | [1, 2] |

map() and filter() both return lazy iterators, not lists — nothing actually runs until you consume them, which is why every example above wraps the call in list() to see the results. filter(None, iterable) is a common idiom for dropping every falsy value (0, "", None, False, empty containers) from an iterable in one line.

enumerate() pairs each item with its index, which avoids the error-prone pattern of manually tracking a counter in a loop. It accepts a start argument for when counting should begin somewhere other than 0:

Python
words = ["kiwi", "fig", "banana"]

for i, word in enumerate(words, start=1):
    print(i, word)
Output
1 kiwi
2 fig
3 banana

sorted() accepts a key function that computes what to sort by, without changing the values themselves — a frequent pattern for sorting by length, case-insensitively, or by a dict field:

Python
sorted(["kiwi", "fig", "banana"], key=len)          # ['fig', 'kiwi', 'banana']
sorted(["Bob", "ann", "Cy"], key=str.lower)            # ['ann', 'Bob', 'Cy']

zip() walks several iterables together and stops as soon as the shortest one runs out — it's the standard way to loop over two related lists in lockstep instead of indexing into both by hand:

Python
names = ["Ann", "Bo", "Cy"]
scores = [90, 85, 77]

for name, score in zip(names, scores):
    print(name, score)
Output
Ann 90
Bo 85
Cy 77

A few more that come up constantly: sum(), min(), max(), abs(), and round().

Python
sum([1, 2, 3, 4])         # 10
max([1, 2, 3, 4])            # 4
min([1, 2, 3, 4])               # 1
abs(-7)                            # 7
round(3.14159, 2)                     # 3.14

Exception Handling

try runs code that might fail; except catches a specific error type if it happens; else runs only when no exception occurred; finally always runs last, whether or not there was an error — useful for cleanup that must happen no matter what.

Python
def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Cannot divide by zero")
        return None
    else:
        print("Division succeeded")
        return result
    finally:
        print("Done attempting division")

print(safe_divide(10, 2))
print(safe_divide(10, 0))
Output
Division succeeded
Done attempting division
5.0
Cannot divide by zero
Done attempting division
None

Notice that finally runs even though both calls hit a return earlier in the function — Python finishes the finally block before the function actually hands the value back to its caller.

A single except can list several exception types in a tuple, which is handy when different kinds of bad input should be handled the same way:

Python
def parse_number(text):
    try:
        return int(text)
    except (ValueError, TypeError) as e:
        print(f"Could not parse {text!r}: {e}")
        return 0

print(parse_number("42"))
print(parse_number("abc"))
Output
42
Could not parse 'abc': invalid literal for int() with base 10: 'abc'
0

Use raise to signal your own error conditions, and a custom exception class (subclassing Exception) when built-in exception types don't describe the problem precisely enough:

Python
class InsufficientFundsError(Exception):
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientFundsError(f"Cannot withdraw {amount}, balance is {balance}")
    return balance - amount

try:
    withdraw(100, 150)
except InsufficientFundsError as e:
    print(f"Transaction failed: {e}")
Output
Transaction failed: Cannot withdraw 150, balance is 100

File Handling

open(path, mode) returns a file object. Wrapping it in a with block guarantees the file gets closed automatically once the block ends — even if an exception is raised inside it — so you should almost never need to call .close() yourself.

Python
with open("notes.txt", "w") as f:
    f.write("First line\n")
    f.write("Second line\n")

with open("notes.txt", "r") as f:
    content = f.read()
print(content, end="")

with open("notes.txt", "r") as f:
    lines = f.readlines()
print(lines)

with open("notes.txt", "a") as f:
    f.write("Third line\n")

with open("notes.txt", "r") as f:
    for line in f:
        print(line.strip())
Output
First line
Second line
['First line\n', 'Second line\n']
First line
Second line
Third line

That example touches every common mode: "w" creates the file (or wipes it if it already existed), "r" reads it back, "a" appends without disturbing what's already there, and looping directly over the file object (for line in f) hands you one line at a time — each still carrying its trailing "\n", which is why .strip() shows up so often alongside it. readlines() differs from .read() in that it returns a list of individual lines rather than one long string.

Reading a file that doesn't exist raises FileNotFoundError, so file-reading code is often paired with a try/except:

Python
try:
    with open("missing.txt", "r") as f:
        data = f.read()
except FileNotFoundError:
    data = ""
    print("File not found — using default data")
Output
File not found — using default data

If you're running these examples in a browser-based Python environment, keep in mind file operations usually go against an in-memory filesystem that resets between sessions rather than your real disk — the behavior is otherwise identical to a normal Python installation.

Try any snippet with your own values in the Python playground.