Skip to main content

Python Quick Reference

For SRE, SDET & SDE Professionalsโ€‹

A copy-paste reference for everyday Python 3. Every example shows its result after # =>.

How to use this page

This page is for looking things up, not for learning from scratch. New to Python? Start with the Python cheat sheet or Python Fundamentals. For step-by-step lessons and mini-projects, follow the Python learning path.

Quick Navigationโ€‹

Language: Getting Started ยท Built-in Data Types ยท Advanced Data Types ยท Strings ยท F-Strings ยท Lists ยท Flow Control ยท Loops ยท Functions ยท Modules & Imports ยท File Handling ยท Classes & Inheritance ยท Miscellaneous

Professional toolkit: Regex ยท Testing (pytest) ยท Logging ยท Performance ยท Gotchas ยท Common Patterns ยท One-Liners


GETTING STARTEDโ€‹

Introductionโ€‹

Hello Worldโ€‹

print("Hello, World!")   # => Hello, World!

Variablesโ€‹

age = 18        # age is of type int
name = "John" # name is now of type str
print(name)

In Python you create a variable by giving it a value โ€” there is no separate "declare" step.

Type Hintsโ€‹

def greet(name: str, age: int) -> str:
return f"{name} is {age}"

Data Typesโ€‹

TypeCategory
strText
int, float, complexNumeric
list, tuple, rangeSequence
dictMapping
set, frozensetSet
boolBoolean
bytes, bytearray, memoryviewBinary (raw bytes, e.g. file or network data)

frozenset is a set that can't be changed; complex is a number with an imaginary part (rarely needed).

See: Built-in Data Types

Arithmeticโ€‹

result = 10 + 30   # => 40
result = 40 - 10 # => 30
result = 50 * 5 # => 250
result = 16 / 4 # => 4.0 (true division, always float)
result = 16 // 4 # => 4 (floor division)
result = 25 % 2 # => 1 (modulo)
result = 5 ** 3 # => 125 (power)

/ is the quotient of x and y; // is the floored quotient (-7 // 2 == -4).

Plus-Equalsโ€‹

counter = 0
counter += 10 # => 10 (same as counter = counter + 10)

message = "Part 1."
message += "Part 2." # => "Part 1.Part 2."

Truthinessโ€‹

if items:      # Same as: if len(items) > 0
process(items)

if not error: # Same as: if error is None or error == ""
continue_processing()

bool(0), bool(""), bool([]), bool(None) # => all False

Guard Clausesโ€‹

def process(user):
if not user:
return None # Early exit
if not user.is_active:
return None
return user.get_profile() # Main logic last

BUILT-IN DATA TYPESโ€‹

Stringsโ€‹

hello = "Hello World"
hello = 'Hello World'
multi_string = """Multiline strings
can span several lines"""

See: Strings

Numbersโ€‹

x = 1      # int
y = 2.8 # float
z = 1j # complex
type(x) # => <class 'int'>
big = 1_000_000 # underscores for readability

Booleansโ€‹

my_bool = True
my_bool = False
bool(0) # => False
bool(1) # => True

Listsโ€‹

list1 = ["apple", "banana", "cherry"]
list2 = [True, False, False]
list3 = [1, 5, 7, 9, 3]
list4 = list((1, 5, 7, 9, 3))

See: Lists

Tupleโ€‹

my_tuple = (1, 2, 3)
my_tuple = tuple((1, 2, 3))
single = (1,) # Trailing comma needed for a 1-item tuple

Like a list, but immutable โ€” it can't be changed after it is created.

Setโ€‹

set1 = {"a", "b", "c"}
set2 = set(("a", "b", "c"))
empty = set() # {} is an empty dict, not a set

a, b = {1, 2, 3}, {2, 3, 4}
a | b # => {1, 2, 3, 4} union
a & b # => {2, 3} intersection
a - b # => {1} difference

Unordered collection of unique items.

Dictionaryโ€‹

empty_dict = {}
a = {"one": 1, "two": 2, "three": 3}
a["one"] # => 1
a.keys() # => dict_keys(['one', 'two', 'three'])
a.values() # => dict_values([1, 2, 3])
a.items() # => dict_items([('one', 1), ...])
a.update({"four": 4})
a["four"] # => 4

Key: value pairs, a JSON-like object.

Safe Dict Accessโ€‹

value = d.get('key', default)           # Won't raise KeyError
d.setdefault('key', default) # Set if missing
merged = {**dict1, **dict2} # Merge (or: dict1 | dict2, 3.9+)

Castingโ€‹

# Integers
int(1) # => 1
int(2.8) # => 2 (truncates)
int("3") # => 3

# Floats
float(1) # => 1.0
float(2.8) # => 2.8
float("3") # => 3.0
float("4.2") # => 4.2

# Strings
str("s1") # => 's1'
str(2) # => '2'
str(3.0) # => '3.0'

Lists vs Tuples vs Sets vs Dictsโ€‹

TypeLooks likeCan change?Keeps order?Use forWatch out
list[1, 2]โœ“โœ“Ordered data.copy() doesn't copy lists inside it
tuple(1, 2)โœ—โœ“Fixed groups, dict keys, returning several valuesOne item needs a comma: (1,)
set{1, 2}โœ“โœ—Unique items, fast "is it in?" checksNo order
dict{"a": 1}โœ“โœ“Looking up a value by keyKeys must be unchangeable types (str, int, tuple)

Performance Characteristicsโ€‹

O(1) means "same speed however big the collection is"; O(n) means "slower the more items there are" (n = number of items).

Operationlistsetdict
Add an itemFast (end only)FastFast
Insert/remove at the frontSlow โ€” shifts every itemโ€“โ€“
Check x in ...Slow โ€” checks one by oneFastFast (by key)
Get by key/positionFast (by position)โ€“Fast

โ†’ To check "is X in here?" many times, use a set or dict, not a list.


ADVANCED DATA TYPESโ€‹

Heapsโ€‹

import heapq

my_list = [9, 5, 4, 1, 3, 2]
heapq.heapify(my_list) # Turn my_list into a min-heap, in place
my_list[0] # => 1 (smallest is always first)
heapq.heappush(my_list, 10) # Insert 10
heapq.heappop(my_list) # => 1 (pop and return smallest)

heapq.nsmallest(3, data) # 3 smallest items
heapq.nlargest(3, data) # 3 largest items

Get the largest first (a "max-heap")โ€‹

heapq always puts the smallest first. Store negative numbers to flip it:

my_list = [-val for val in [9, 5, 4, 1, 3, 2]]
heapq.heapify(my_list)
-heapq.heappop(my_list) # => 9

A heap is a list kept in a special order so the smallest item is always at index 0. Adding and removing items stays fast even for big lists. See: heapq

Stacks and Queuesโ€‹

from collections import deque

q = deque() # empty
q = deque([1, 2, 3]) # with values
q.append(4) # add to right => deque([1, 2, 3, 4])
q.appendleft(0) # add to left => deque([0, 1, 2, 3, 4])
q.pop() # => 4 (remove from right: stack)
q.popleft() # => 0 (remove from left: queue)
q.rotate(1) # rotate 1 step right: deque([1, 2, 3]) => deque([3, 1, 2])

recent = deque(maxlen=100) # Bounded: oldest items drop off (handy for last-N logs)

deque (say "deck") is a double-ended queue: adding and removing at either end is fast. Use it for stacks and queues instead of list.pop(0). See: deque

Counter & defaultdictโ€‹

from collections import Counter, defaultdict

Counter(["a", "b", "a"]) # => Counter({'a': 2, 'b': 1})
Counter(words).most_common(3) # Top 3

by_status = defaultdict(list)
by_status["FAIL"].append("test_login") # No KeyError on first access

STRINGSโ€‹

Array-likeโ€‹

hello = "Hello, World"
hello[1] # => 'e'
hello[-1] # => 'd'

Loopingโ€‹

for char in "foo":
print(char) # f, o, o

Slicing Stringโ€‹

 โ”Œโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”
| m | y | b | a | c | o | n |
โ””โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”˜
0 1 2 3 4 5 6 7
-7 -6 -5 -4 -3 -2 -1
s = 'mybacon'
s[2:5] # => 'bac'
s[0:2] # => 'my'
s[:2] # => 'my'
s[2:] # => 'bacon'
s[:2] + s[2:] # => 'mybacon'
s[:] # => 'mybacon'
s[-5:-1] # => 'baco'

With a strideโ€‹

s = '12345' * 5   # => '1234512345123451234512345'
s[::5] # => '11111'
s[4::5] # => '55555'
s[::-5] # => '55555'
s[::-1] # => '5432154321543215432154321' (reverse)

String Lengthโ€‹

len("Hello, World!")   # => 13

Multiple Copiesโ€‹

'===+' * 8   # => '===+===+===+===+===+===+===+===+'

Check Stringโ€‹

'spam' in 'I saw spamalot!'           # => True
'spam' not in 'I saw The Holy Grail!' # => True

Concatenatesโ€‹

s, t = 'spam', 'egg'
s + t # => 'spamegg'
'spam' 'egg' # => 'spamegg' (adjacent literals are joined)

Formattingโ€‹

name, age = "John", 23
"Hello, %s!" % name # %-style (legacy)
"%s is %d years old." % (name, age)

# format() method
"My name is {fname}, I'm {age}".format(fname="John", age=36)
"My name is {0}, I'm {1}".format("John", 36)
"My name is {}, I'm {}".format("John", 36)

Prefer f-strings in new code. In logging calls, keep %s placeholders (see Logging).

Inputโ€‹

name = input("Enter your name: ")   # Always returns a str

Join & Splitโ€‹

"#".join(["John", "Peter", "Vicky"])   # => 'John#Peter#Vicky'
"a,b,c".split(",") # => ['a', 'b', 'c']

Common Methodsโ€‹

"Hello, world!".endswith("!")     # => True
"Hello".startswith("He") # => True
" pad ".strip() # => 'pad'
"Hello".upper() # => 'HELLO'
"Hello".lower() # => 'hello'
"a-b-c".replace("-", "_") # => 'a_b_c'
"hello".find("l") # => 2 (-1 if missing)
"42".isdigit() # => True

F-STRINGSโ€‹

(Python 3.6+)

f-Strings Usageโ€‹

website = 'Quickref.ME'
f"Hello, {website}" # => 'Hello, Quickref.ME'

num = 10
f'{num} + 10 = {num + 10}' # => '10 + 10 = 20'
f"""He said {"I'm John"}""" # => "He said I'm John"
f'5 {"{stars}"}' # => '5 {stars}'
f'{{5}} {"stars"}' # => '{5} stars' (double braces escape)

name, age = 'Eric', 27
f"""Hello!
I'm {name}.
I'm {age}.""" # => "Hello!\n I'm Eric.\n I'm 27."

f"{num=}" # => 'num=10' (debug form, 3.8+)

f-Strings Fill Alignโ€‹

f'{"text":10}'      # => 'text      '  [width]
f'{"test":*>10}' # => '******test' fill left
f'{"test":*<10}' # => 'test******' fill right
f'{"test":*^10}' # => '***test***' fill center
f'{12345:0>10}' # => '0000012345' fill with numbers

f-Strings Typeโ€‹

f'{10:b}'              # => '1010'          binary
f'{10:o}' # => '12' octal
f'{200:x}' # => 'c8' hex
f'{200:X}' # => 'C8'
f'{345600000000:e}' # => '3.456000e+11' scientific
f'{65:c}' # => 'A' character
f'{10:#b}' # => '0b1010' with base prefix
f'{10:#o}' # => '0o12'
f'{10:#x}' # => '0xa'

f-Strings Othersโ€‹

import math

f'{-12345:0=10}' # => '-000012345' negative numbers
f'{12345:010}' # => '0000012345' [0] shortcut (no align)
f'{-12345:010}' # => '-000012345'
f'{math.pi:.2f}' # => '3.14' [.precision]
f'{1000000:,.2f}' # => '1,000,000.00' [grouping_option]
f'{1000000:_.2f}' # => '1_000_000.00'
f'{0.25:0%}' # => '25.000000%' percentage
f'{0.25:.0%}' # => '25%'

f-Strings Signโ€‹

f'{12345:+}'      # => '+12345'      [sign] (+/-)
f'{-12345:+}' # => '-12345'
f'{-12345:+10}' # => ' -12345'
f'{-12345:+010}' # => '-000012345'

LISTSโ€‹

Definingโ€‹

li1 = []                   # => []
li2 = [4, 5, 6] # => [4, 5, 6]
li3 = list((1, 2, 3)) # => [1, 2, 3]
li4 = list(range(1, 11)) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Generateโ€‹

list(filter(lambda x: x % 2 == 1, range(1, 20)))   # => [1, 3, 5, ..., 19]
[x ** 2 for x in range(1, 11) if x % 2 == 1] # => [1, 9, 25, 49, 81]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7]

Comprehensionsโ€‹

# List: [expr for item in iterable if condition]
squares = [x**2 for x in range(10) if x % 2 == 0]

# Dict: {key: value for item in iterable}
by_id = {user['id']: user for user in users}

# Set: {expr for item in iterable}
unique_domains = {email.split('@')[1] for email in emails}

# Generator (lazy, memory-efficient): same syntax, () instead of []
sum_squares = sum(x**2 for x in range(1_000_000))

Appendโ€‹

li = []
li.append(1) # => [1]
li.append(2) # => [1, 2]
li.insert(0, 0) # => [0, 1, 2] (O(n): shifts everything)

List Slicingโ€‹

a_list[start:end]
a_list[start:end:step]

Slicingโ€‹

a = ['spam', 'egg', 'bacon', 'tomato', 'ham', 'lobster']
a[2:5] # => ['bacon', 'tomato', 'ham']
a[-5:-2] # => ['egg', 'bacon', 'tomato']
a[1:4] # => ['egg', 'bacon', 'tomato']

Omitting indexโ€‹

a[:4]        # => ['spam', 'egg', 'bacon', 'tomato']
a[0:4] # => ['spam', 'egg', 'bacon', 'tomato']
a[2:] # => ['bacon', 'tomato', 'ham', 'lobster']
a[2:len(a)] # => ['bacon', 'tomato', 'ham', 'lobster']
a[:] # => shallow copy of the whole list

With a strideโ€‹

a[0:6:2]   # => ['spam', 'bacon', 'ham']
a[1:6:2] # => ['egg', 'tomato', 'lobster']
a[6:0:-2] # => ['lobster', 'tomato', 'egg']
a[::-1] # => ['lobster', 'ham', 'tomato', 'bacon', 'egg', 'spam']

Removeโ€‹

li = ['bread', 'butter', 'milk']
li.pop() # => 'milk' li is now ['bread', 'butter']
del li[0] # li is now ['butter']
li.remove('butter') # Remove first matching value (ValueError if missing)
li.clear() # => []

Accessโ€‹

li = ['a', 'b', 'c', 'd']
li[0] # => 'a'
li[-1] # => 'd'
li[4] # IndexError: list index out of range

Concatenatingโ€‹

odd = [1, 3, 5]
odd.extend([9, 11, 13]) # In place => [1, 3, 5, 9, 11, 13]

odd = [1, 3, 5]
odd + [9, 11, 13] # New list => [1, 3, 5, 9, 11, 13]

Sort & Reverseโ€‹

li = [3, 1, 3, 2, 5]
li.sort() # In place => [1, 2, 3, 3, 5]
li.reverse() # In place => [5, 3, 3, 2, 1]
sorted(li) # Returns a new sorted list
sorted(users, key=lambda u: u['age'], reverse=True)

Countโ€‹

[3, 1, 3, 2, 5].count(3)   # => 2

Repeatingโ€‹

["re"] * 3    # => ['re', 're', 're']

FLOW CONTROLโ€‹

Basicโ€‹

num = 5
if num > 10:
print("num is totally bigger than 10.")
elif num < 10:
print("num is smaller than 10.")
else:
print("num is indeed 10.")

One Lineโ€‹

a, b = 330, 200
r = "a" if a > b else "b" # => 'a' (conditional expression)

else ifโ€‹

value = True
if not value:
print("Value is False")
elif value is None: # Use `is` for None, not ==
print("Value is None")
else:
print("Value is True")

match (3.10+)โ€‹

match status_code:
case 200:
print("OK")
case 404 | 410:
print("Gone")
case _:
print("Other")

LOOPSโ€‹

Basicโ€‹

primes = [2, 3, 5, 7]
for prime in primes:
print(prime) # 2 3 5 7

With Indexโ€‹

animals = ["dog", "cat", "mouse"]
for i, value in enumerate(animals): # enumerate() adds a counter
print(i, value) # 0 dog / 1 cat / 2 mouse

Whileโ€‹

x = 0
while x < 4:
print(x) # 0 1 2 3
x += 1

Breakโ€‹

for index in range(10):
if index == 5:
break
print(index * 10) # 0 10 20 30 40

Continueโ€‹

for index in range(3, 8):
if index == 5:
continue
print(index * 10) # 30 40 60 70

Rangeโ€‹

for i in range(4):        print(i)   # 0 1 2 3
for i in range(4, 8): print(i) # 4 5 6 7
for i in range(4, 10, 2): print(i) # 4 6 8

With zip()โ€‹

words = ['Mon', 'Tue', 'Wed']
nums = [1, 2, 3]
for w, n in zip(words, nums): # Pairs items; stops at the shortest
print(f'{n}:{w}') # 1:Mon 2:Tue 3:Wed

for/elseโ€‹

nums = [60, 70, 30, 110, 90]
for n in nums:
if n > 100:
print(f"{n} is bigger than 100")
break
else:
print("Not found!") # Runs only if the loop did NOT break

Dict Iterationโ€‹

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

FUNCTIONSโ€‹

Basicโ€‹

def hello_world():
print('Hello, World!')

Returnโ€‹

def add(x, y):
return x + y

add(5, 6) # => 11

Positional Argumentsโ€‹

def varargs(*args):
return args

varargs(1, 2, 3) # => (1, 2, 3)

Keyword Argumentsโ€‹

def keyword_args(**kwargs):
return kwargs

keyword_args(big="foot", loch="ness") # => {'big': 'foot', 'loch': 'ness'}

*args and **kwargs Togetherโ€‹

def func(*args, **kwargs):
print(args) # (1, 2, 3) - tuple
print(kwargs) # {'a': 1} - dict

func(1, 2, 3, a=1)

Returning Multipleโ€‹

def swap(x, y):
return y, x # Returns a tuple

x, y = swap(1, 2) # => x = 2, y = 1

Default Valueโ€‹

def add(x, y=10):
return x + y

add(5) # => 15
add(5, 20) # => 25

Never use a mutable default like []. See Gotchas.

Anonymous Functions (Lambda)โ€‹

(lambda x: x > 2)(3)                    # => True
(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5

# Good: simple, one-off transformations
sorted_users = sorted(users, key=lambda u: u['age'])
# Bad: complex logic โ€” use a named function instead

Closuresโ€‹

def rate_limiter(max_calls: int):
calls = 0
def check():
nonlocal calls
if calls >= max_calls:
raise Exception("Rate limit exceeded")
calls += 1
return check

api_limiter = rate_limiter(100) # Each closure has its own state

Decoratorsโ€‹

import functools

def decorator(func):
@functools.wraps(func) # Preserves __name__, docstring
def wrapper(*args, **kwargs):
# before
result = func(*args, **kwargs)
# after
return result
return wrapper

@decorator
def my_func(): ...

# Decorator with parameters
def retry(max_attempts=3):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception:
if attempt == max_attempts - 1:
raise
return wrapper
return decorator

@retry(max_attempts=3)
def fetch_data(): ...

MODULES & IMPORTSโ€‹

Import Modulesโ€‹

import math
math.sqrt(16) # => 4.0

From a Moduleโ€‹

from math import ceil, floor
ceil(3.7) # => 4
floor(3.7) # => 3

Import Allโ€‹

from math import *   # Avoid โ€” pollutes the namespace

Shorten Moduleโ€‹

import math as m
math.sqrt(16) == m.sqrt(16) # => True

from module import func as f # Alias a single name

Functions and Attributesโ€‹

import math
dir(math) # List everything the module exposes
help(math.sqrt) # Show the docstring

Main Guardโ€‹

if __name__ == "__main__":
main() # Only runs when script is executed directly, not on import

FILE HANDLINGโ€‹

Read Fileโ€‹

Line by lineโ€‹

with open("myfile.txt", "r", encoding="utf8") as file:
for line in file: # Streams: doesn't load the whole file
print(line.rstrip("\n"))

With line numberโ€‹

with open("myfile.txt") as file:
for i, line in enumerate(file, start=1):
print(f"Number {i}: {line}", end="")

Stringโ€‹

Write a stringโ€‹

contents = {"aa": 12, "bb": 21}
with open("myfile1.txt", "w+") as file:
file.write(str(contents))

Read a stringโ€‹

with open("myfile1.txt", "r+") as file:
contents = file.read()
print(contents)

Object (JSON)โ€‹

Write an objectโ€‹

import json

contents = {"aa": 12, "bb": 21}
with open("myfile2.txt", "w+") as file:
json.dump(contents, file, indent=2)

Read an objectโ€‹

with open("myfile2.txt", "r+") as file:
contents = json.load(file)
print(contents)

File Modesโ€‹

ModeMeaning
rRead (default)
wWrite, truncating first
aAppend
xCreate, fail if exists
+Read and write (r+, w+)
bBinary (rb, wb)

Delete a Fileโ€‹

import os
os.remove("myfile.txt")

Check and Deleteโ€‹

import os

if os.path.exists("myfile.txt"):
os.remove("myfile.txt")
else:
print("The file does not exist")

Delete Folderโ€‹

import os, shutil

os.rmdir("myfolder") # Only works if the folder is empty
shutil.rmtree("myfolder") # Deletes the folder and everything in it

Modern (pathlib)โ€‹

from pathlib import Path

Path("file.txt").read_text()
Path("file.txt").write_text("content")
Path("file.txt").exists()
Path("file.txt").unlink(missing_ok=True) # Delete (3.8+)
Path("data").mkdir(parents=True, exist_ok=True)
json_files = list(Path("data").glob("*.json"))

JSON with pathlibโ€‹

import json

data = json.loads(Path("config.json").read_text())
Path("output.json").write_text(json.dumps(data, indent=2))

CSVโ€‹

import csv

with open("data.csv") as f:
reader = csv.DictReader(f) # dict per row
for row in reader:
print(row['name'], row['age'])

with open("output.csv", "w", newline='') as f:
writer = csv.DictWriter(f, fieldnames=['name', 'age'])
writer.writeheader()
writer.writerows([{'name': 'Alice', 'age': 30}])

CLASSES & INHERITANCEโ€‹

Definingโ€‹

class MyNewClass:
pass

my = MyNewClass() # Class instantiation

Constructorsโ€‹

class Animal:
def __init__(self, voice):
self.voice = voice

cat = Animal('Meow')
cat.voice # => 'Meow'
dog = Animal('Woof')
dog.voice # => 'Woof'

Basic Classโ€‹

class User:
def __init__(self, id: int, name: str):
self.id = id
self.name = name

def __str__(self) -> str:
return f"User({self.name})"

def __eq__(self, other) -> bool:
return isinstance(other, User) and self.id == other.id

Methodโ€‹

class Dog:
def bark(self): # Method of the class
print("Ham-Ham")

charlie = Dog()
charlie.bark() # => Ham-Ham

Class Variablesโ€‹

class MyClass:
class_variable = "A class variable!" # Shared by all instances

MyClass.class_variable # => 'A class variable!'
x = MyClass()
x.class_variable # => 'A class variable!'

Class & Static Methodsโ€‹

class User:
count = 0

@classmethod
def from_dict(cls, data: dict) -> "User": # Alternative constructor
return cls(**data)

@staticmethod
def is_valid_email(email: str) -> bool: # No self/cls needed
return "@" in email

Super() Functionโ€‹

class ParentClass:
def print_test(self):
print("Parent Method")

class ChildClass(ParentClass):
def print_test(self):
print("Child Method")
super().print_test() # Calls the parent's print_test()

ChildClass().print_test()
# => Child Method
# => Parent Method

repr() Methodโ€‹

class Employee:
def __init__(self, name):
self.name = name

def __repr__(self):
return f"Employee({self.name!r})"

john = Employee('John')
print(john) # => Employee('John')

User-defined Exceptionsโ€‹

class CustomError(Exception):
pass

See: Handle Exceptions

Polymorphismโ€‹

class ParentClass:
def print_self(self):
print('A')

class ChildClass(ParentClass):
def print_self(self):
print('B')

for obj in (ParentClass(), ChildClass()):
obj.print_self() # => A, then B

Overridingโ€‹

class ParentClass:
def print_self(self):
print("Parent")

class ChildClass(ParentClass):
def print_self(self):
print("Child")

ChildClass().print_self() # => Child

Inheritanceโ€‹

class Animal:
def __init__(self, name, legs):
self.name = name
self.legs = legs

class Dog(Animal):
def sound(self):
print("Woof!")

yoki = Dog("Yoki", 4)
yoki.name # => 'Yoki'
yoki.legs # => 4
yoki.sound() # => Woof!

class Admin(User):
def __init__(self, id: int, name: str, level: int):
super().__init__(id, name) # Call parent constructor
self.level = level

Dataclass (Modern Alternative)โ€‹

from dataclasses import dataclass, field

@dataclass
class User:
id: int
name: str
tags: list = field(default_factory=list)
# Generates __init__, __repr__, __eq__ automatically

Propertiesโ€‹

class User:
def __init__(self, email: str):
self._email = email

@property
def email(self) -> str:
return self._email

@email.setter
def email(self, value: str) -> None:
if "@" not in value:
raise ValueError("Invalid email")
self._email = value

Special Methods (Dunder)โ€‹

MethodUseExample
__init__ConstructorUser(id, name)
__str__Human readableprint(user)
__repr__Developer viewrepr(user)
__eq__Equalityuser1 == user2
__lt__Sortingsorted(users)
__len__Lengthlen(user)
__hash__Dict/set key{user: value}
__enter__/__exit__Context managerwith resource() as r:

MISCELLANEOUSโ€‹

Commentsโ€‹

# This is a single-line comment.

""" Multiline strings can be written
using three "s, and are often used
as documentation (docstrings).
"""

''' Multiline strings can also be written
using three 's.
'''

Generatorsโ€‹

When to use: large data, streaming, memory efficiency. Generators make your code lazy.

def double_numbers(iterable):
for i in iterable:
yield i + i

def count_up(max):
current = 1
while current <= max:
yield current # Pauses here, resumes on next()
current += 1

for num in count_up(1_000_000): # Doesn't load all values in memory
process(num)

# Generator expression (lazy list comprehension)
squares = (x**2 for x in range(1_000_000))

Generator to Listโ€‹

values = (-x for x in [1, 2, 3, 4, 5])
list(values) # => [-1, -2, -3, -4, -5]

A generator can be consumed only once.

Handle Exceptionsโ€‹

try:
# Use "raise" to raise an error
raise IndexError("This is an index error")
except IndexError as e:
pass # No-op. Usually you would recover here
except (TypeError, NameError):
pass # Handle several exceptions together
except Exception as e:
print(f"Unexpected: {e}")
raise # Re-raise if you can't handle it
else: # Optional; must follow all except blocks
print("All good!") # Runs only if try raised no exception
finally: # Runs under all circumstances
print("We can clean up resources here")

Custom Exceptions & Chainingโ€‹

class ValidationError(Exception):
pass

try:
result = int(user_input)
except ValueError as e:
raise ValidationError("Invalid number") from e # Preserves original traceback

Context Managersโ€‹

File handling (automatic cleanup)โ€‹

with open("file.txt") as f:
data = f.read()
# File automatically closed, even on exception

with open("in.txt") as fin, open("out.txt", "w") as fout:
fout.write(fin.read().upper())

Custom context managerโ€‹

class Resource:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
cleanup()
return False # Re-raise any exception

# Function-based (simpler)
from contextlib import contextmanager

@contextmanager
def database_connection(host: str):
conn = create_connection(host)
try:
yield conn
finally:
conn.close()

REGEXโ€‹

import re

# Match / search
if re.match(r'^\d{3}-\d{3}-\d{4}$', "555-123-4567"):
print("Valid phone")

# Find all
emails = re.findall(r'\w+@\w+\.\w+', text)

# Replace
text = re.sub(r'\[NAME\]', 'Alice', template)

# Named groups
m = re.search(r"(?P<year>\d{4})-(?P<month>\d{2})", "2024-01")
m.group('year') # '2024'

# Compile for reuse โ€” important in loops!
PATTERN = re.compile(r'pattern')
for line in lines:
if PATTERN.search(line):
process(line)

Common Patternsโ€‹

PatternMatches
\dDigit
\wWord char (letter, digit, _)
\sWhitespace
[abc]Any of a, b, c
[^abc]NOT a, b, c
+1 or more
*0 or more
?0 or 1
{3}Exactly 3
^ / $Start / end of string

TESTING (pytest)โ€‹

Basicsโ€‹

import pytest

def test_my_function():
assert my_function(5) == 10

def test_error_handling():
with pytest.raises(ValueError):
my_function(-1)

Fixturesโ€‹

@pytest.fixture
def user():
return User(id=1, name="Alice")

@pytest.fixture(scope="session") # function (default) | class | module | session
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()

def test_user_creation(user):
assert user.name == "Alice"

Parametrizationโ€‹

@pytest.mark.parametrize("input,expected", [
(2, 4), (3, 9), (5, 25),
])
def test_square(input, expected):
assert square(input) == expected

Markersโ€‹

@pytest.mark.skip(reason="Not yet implemented")
def test_future(): ...

@pytest.mark.slow
def test_large_dataset(): ...

# Run subsets: pytest -m smoke | pytest -m "not slow"

Mockingโ€‹

from unittest.mock import patch, Mock

@patch('module.function') # Patch where it's USED, not where it's defined
def test_with_mock(mock_func):
mock_func.return_value = "mocked"
assert my_code() == "mocked"
mock_func.assert_called_once()

# Side effects (different result per call, or raise)
mock = Mock(side_effect=[ValueError("boom"), "success"])

LOGGINGโ€‹

import logging

logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

logger.info("Starting...")
logger.error("Failed: %s", error_message)
DEBUG < INFO < WARNING < ERROR < CRITICAL

PERFORMANCE TIPSโ€‹

What's Fast?โ€‹

  • โœ“ Looking up a key in a dict or an item in a set
  • โœ“ Adding to the end of a list
  • โœ— Inserting/removing at the front of a list โ†’ use collections.deque
  • โœ— x in some_list on a big list โ†’ use a set instead
  • Need the smallest/largest item again and again while the data changes โ†’ use heapq

Profilingโ€‹

import timeit
time = timeit.timeit("x = [1,2,3] + [4,5,6]", number=100_000)

import cProfile, pstats
cProfile.run('main()', 'profile_out')
pstats.Stats('profile_out').sort_stats('cumulative').print_stats(10)

GOTCHAS (Watch Out!)โ€‹

1. Mutable Default Argumentsโ€‹

# BAD โ€” same list reused across every call
def func(items=[]):
items.append(1)
return items

# GOOD
def func(items=None):
if items is None:
items = []
return items

2. Shallow Copyโ€‹

a = [[1, 2], [3, 4]]
b = a.copy() # Shallow copy โ€” inner lists still shared
b[0][0] = 99 # Changes both a and b
import copy
b = copy.deepcopy(a) # Fix

3. Late Binding in Closuresโ€‹

A function made in a loop looks up the loop variable when it runs, not when it was made.

# BAD โ€” all lambdas capture the same final value of i
functions = [lambda x: x + i for i in range(3)]

# GOOD โ€” bind i at definition time via default arg
functions = [lambda x, j=i: x + j for i in range(3)]

4. Strings Can't Be Changed in Placeโ€‹

s = "hello"
s[0] = 'H' # TypeError!
s = "H" + s[1:] # Correct way to "modify"

5. Dict Keys Must Be Unchangeable ("Hashable")โ€‹

Only values that can't change โ€” str, int, tuple โ€” can be dict keys or set items.

d = {[1, 2]: "value"}   # TypeError โ€” a list can change, so it can't be a key
d = {(1, 2): "value"} # OK โ€” a tuple can't change

6. / Always Returns a Floatโ€‹

16 / 4    # => 4.0, not 4
-7 // 2 # => -4 (floors toward negative infinity, not toward zero)

7. [[]] * n Shares One Inner Listโ€‹

grid = [[]] * 3
grid[0].append(1) # => [[1], [1], [1]] (all the same list!)
grid = [[] for _ in range(3)] # Fix

COMMON PATTERNSโ€‹

Error Handling with Contextโ€‹

try:
operation()
except SpecificError as e:
handle_specific(e)
except Exception as e:
logger.error("Unexpected: %s", e)
raise
else:
logger.info("Success")
finally:
cleanup()

Type Checkingโ€‹

from typing import Optional

def process(items: list[str]) -> dict[str, int]: # Built-in generics, 3.9+
return {item: len(item) for item in items}

def get_value(key: str) -> Optional[str]: # Or: str | None (3.10+)
return data.get(key)

Factory Patternโ€‹

class UserFactory:
@staticmethod
def create(role: str):
if role == "admin":
return AdminUser()
return RegularUser()

One-Liners to Rememberโ€‹

TaskCode
Check if dict has keyif key in d:
Get dict value or defaultd.get(key, default)
Convert to listlist(iterable)
Sort descendingsorted(items, reverse=True)
Remove duplicates (order lost)list(set(items))
Remove duplicates (order kept)list(dict.fromkeys(items))
Join strings", ".join(items)
Split stringtext.split(", ")
Count occurrencestext.count("word")
Replace texttext.replace("old", "new")
Format stringf"Hello {name}"
Reverse a stringtext[::-1]
Swap two variablesa, b = b, a
Conditional valuex = "yes" if cond else "no"
All conditions trueall([a, b, c])
Any condition trueany([a, b, c])
Find minimum / maximummin(items) / max(items)
Sum all itemssum(items)
Index of itemitems.index(value)
Reverse listitems[::-1]
Get unique itemsset(items)
Pair two listsdict(zip(keys, values))
Top-N largestheapq.nlargest(n, items)
Count itemsCounter(items).most_common()
Flatten one level[x for sub in nested for x in sub]

Need More Detail?โ€‹

This page is the quick answer. For the longer explanation behind each topic โ€” concurrency, the memory model, ops scripting, and interview questions โ€” see Python: The Complete Guide, the same guide the cheat sheet links to. For an ordered plan with mini-projects, follow the Python learning path.

Last updated: September 2026