Python Concepts¶
109 cards — 🟢 55 easy | 🟡 37 medium | 🔴 2 hard
🟢 Easy (55)¶
1. if / elif / else
Show answer
x = 10if x > 20:
print("big")
elif x > 5:
print("medium") # ← this runs
else:
print("small")
Conditions are evaluated top-to-bottom; first True branch wins. elif and else are optional.
2. for loop
Show answer
for name in ["alice", "bob", "carol"]:print(name.upper())
Iterates over any iterable: lists, strings, dicts, files, generators, range().
3. while loop
Show answer
n = 5while n > 0:
print(n)
n -= 1
Runs as long as the condition is True. Use break to exit early.
4. break
Show answer
for n in range(100):if n > 4:
break
print(n) # prints 0 1 2 3 4
Exits the innermost loop immediately.
5. continue
Show answer
for n in range(5):if n == 2:
continue
print(n) # prints 0 1 3 4
Skips the rest of the current iteration, jumps to the next one.
6. pass
Show answer
class NotYet:pass # placeholder — class body can't be empty
def todo():
pass # same for functions
Does nothing. Used as a syntactic placeholder.
7. def (function definition)
Show answer
def greet(name):return f"hello, {name}"
print(greet("alice")) # hello, alice
Functions are first-class objects — you can pass them, return them, store them.
8. lambda (anonymous function)
Show answer
square = lambda x: x ** 2print(square(5)) # 25
sorted(["bob", "alice"], key=lambda s: len(s))
# ['bob', 'alice']
Single-expression functions. No statements, no multi-line.
9. return
Show answer
def add(a, b):return a + b
result = add(3, 4) # 7
Sends a value back to the caller and exits the function. Without return, function returns None.
10. class
Show answer
class Dog:def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} says woof"
d = Dog("Rex")
print(d.speak()) # Rex says woof
11. self
Show answer
class Counter:def __init__(self):
self.count = 0
def inc(self):
self.count += 1
self is the instance — it's how methods access the object's data. Not a keyword, but always the first parameter.
12. try / except
Show answer
try:x = int("not a number")
except ValueError as e:
print(f"bad input: {e}")
Catch specific exceptions. Avoid bare except: — always name the exception type.
13. raise
Show answer
def divide(a, b):if b == 0:
raise ValueError("cannot divide by zero")
return a / b
Raise an exception explicitly. raise without args re-raises the current exception in an except block.
14. assert
Show answer
def avg(nums):assert len(nums) > 0, "empty list"
return sum(nums) / len(nums)
Raises AssertionError if condition is False. Disabled with python -O. Use for sanity checks, not input validation.
15. with (context manager)
Show answer
with open("file.txt") as f:data = f.read()
# f is automatically closed here
Guarantees cleanup (close, unlock, etc.) even if an exception occurs. Calls __enter__ and __exit__ under the hood.
16. as (aliasing)
Show answer
import numpy as np # module aliaswith open("f") as fh: # bind context manager result
except KeyError as e: # bind exception to name
as binds a name in import, with, and except statements.
17. in / not in (membership)
Show answer
print(3 in [1, 2, 3]) # Trueprint("x" not in "hello") # True
print("key" in {"key": 1}) # True (checks keys)
Works on lists, tuples, sets, dicts (keys), strings (substring).
18. is / is not (identity)
Show answer
x = Noneif x is None:
print("nothing here")
a = [1, 2]
b = a
print(a is b) # True (same object)
print(a is [1,2]) # False (different object, same value)
is checks identity (same object in memory). Use == for value equality.
19. del (delete)
Show answer
x = 42del x # unbinds the name
lst = [1, 2, 3]
del lst[1] # [1, 3]
d = {"a": 1}
del d["a"] # {}
Removes a name binding, list element, dict entry, or attribute.
20. None
Show answer
def find(lst, target):for item in lst:
if item == target:
return item
return None # explicit "not found"
result = find([1,2,3], 99)
if result is None:
print("not found")
Python's null. There is exactly one None object. Always test with is None, not ==.
21. f-string (formatted string literal)
Show answer
name = "alice"age = 30
print(f"{name} is {age} years old")
print(f"{age:>10}") # right-align, width 10
print(f"{3.14159:.2f}") # 3.14
print(f"{1000:,}") # 1,000
Expressions inside {} are evaluated at runtime. Python 3.6+.
22. Common string methods
Show answer
s = " Hello, World "s.strip() # "Hello, World"
s.lower() # " hello, world "
s.split(", ") # [' Hello', 'World ']
"_".join(["a","b"]) # "a_b"
s.replace("World", "Python")
s.startswith(" He") # True
s.find("World") # 9 (-1 if not found)
"42".isdigit() # True
23. String slicing
Show answer
s = "python"s[0] # 'p'
s[-1] # 'n'
s[2:4] # 'th'
s[:3] # 'pyt'
s[3:] # 'hon'
s[::-1] # 'nohtyp' (reverse)
s[::2] # 'pto' (every other)
Same [start:stop:step] syntax works on lists and tuples.
24. List operations
Show answer
a = [3, 1, 2]a.append(4) # [3, 1, 2, 4]
a.extend([5, 6]) # [3, 1, 2, 4, 5, 6]
a.insert(0, 99) # [99, 3, 1, 2, 4, 5, 6]
a.pop() # returns 6, list shrinks
a.remove(99) # removes first occurrence
a.sort() # in-place: [1, 2, 3, 4, 5]
a.reverse() # in-place: [5, 4, 3, 2, 1]
len(a) # 5
25. List comprehension
Show answer
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
flat = [c for word in ["hi","yo"] for c in word] # ['h','i','y','o']
More readable than map/filter for simple transforms.
26. Dict operations
Show answer
d = {"a": 1, "b": 2}d["c"] = 3 # add/update
d.get("z", 0) # 0 (no KeyError)
d.pop("b") # returns 2, removes key
d.update({"a": 10}) # merge
d.setdefault("d", 4) # insert only if missing
for k, v in d.items():
print(k, v)
27. Dict comprehension
Show answer
squares = {x: x**2 for x in range(5)}# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
inverse = {v: k for k, v in squares.items()}
# {0: 0, 1: 1, 4: 2, 9: 3, 16: 4}
28. Set operations
Show answer
a = {1, 2, 3}b = {2, 3, 4}
a | b # {1, 2, 3, 4} union
a & b # {2, 3} intersection
a - b # {1} difference
a ^ b # {1, 4} symmetric difference
3 in a # True (O(1) lookup)
a.add(5)
a.discard(99) # no error if missing
29. Set comprehension
Show answer
words = ["hello", "world", "hello"]unique_lengths = {len(w) for w in words} # {5}
30. Tuple operations
Show answer
t = (1, "two", 3.0)t[0] # 1
t[-1] # 3.0
len(t) # 3
a, b, c = t # unpacking
# single-element tuple needs trailing comma
one = (42,)
# tuples are immutable — no append, no assignment
# t[0] = 99 → TypeError
31. enumerate()
Show answer
for i, name in enumerate(["alice", "bob", "carol"]):print(f"{i}: {name}")
# 0: alice
# 1: bob
# 2: carol
for i, ch in enumerate("abc", start=1):
print(i, ch) # 1 a, 2 b, 3 c
Replaces the manual counter pattern. Always prefer over range(len(x)).
32. zip()
Show answer
names = ["alice", "bob"]ages = [30, 25]
for name, age in zip(names, ages):
print(f"{name} is {age}")
dict(zip(names, ages)) # {"alice": 30, "bob": 25}
Stops at the shortest iterable. Use itertools.zip_longest to pad.
33. map() and filter()
Show answer
nums = [1, 2, 3, 4, 5]list(map(str, nums)) # ['1','2','3','4','5']
list(filter(lambda x: x>3, nums)) # [4, 5]
Both return lazy iterators. Usually prefer comprehensions:
[str(n) for n in nums]
[n for n in nums if n > 3]
34. any() and all()
Show answer
nums = [2, 4, 6, 7, 8]any(x > 5 for x in nums) # True (at least one)
all(x > 0 for x in nums) # True (every one)
all(x % 2 == 0 for x in nums) # False (7 is odd)
Short-circuit: any() stops at first True, all() stops at first False.
35. sorted() and .sort()
Show answer
nums = [3, 1, 2]sorted(nums) # [1, 2, 3] — returns new list
nums.sort() # in-place, returns None
words = ["banana", "apple", "cherry"]
sorted(words, key=len) # ['apple', 'banana', 'cherry']
sorted(words, key=len, reverse=True) # ['cherry', 'banana', 'apple']
sorted() works on any iterable. .sort() is list-only.
36. isinstance() and type()
Show answer
x = 42isinstance(x, int) # True
isinstance(x, (int, float)) # True (either type)
type(x) #
type(x) is int # True
Prefer isinstance() — it handles subclasses. type() is for exact type checks.
37. Conditional expression (ternary)
Show answer
age = 20status = "adult" if age >= 18 else "minor"
# In a list
labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
# ['even', 'odd', 'even', 'odd', 'even']
38. init and repr
Show answer
class Point:def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point({self.x}, {self.y})"
p = Point(1, 2)
print(p) # Point(1, 2)
__init__ sets up the instance. __repr__ controls how it prints.
39. Inheritance
Show answer
class Animal:def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "woof"
class Cat(Animal):
def speak(self):
return "meow"
for a in [Dog(), Cat()]:
print(a.speak()) # woof, meow
Subclasses override parent methods. Use super() to call the parent.
40. collections.defaultdict
Show answer
from collections import defaultdictcounts = defaultdict(int)
for word in "the cat sat on the mat".split():
counts[word] += 1
# {'the': 2, 'cat': 1, 'sat': 1, 'on': 1, 'mat': 1}
groups = defaultdict(list)
for name, dept in [("alice","eng"), ("bob","eng"), ("carol","hr")]:
groups[dept].append(name)
# {'eng': ['alice','bob'], 'hr': ['carol']}
41. collections.Counter
Show answer
from collections import Counterc = Counter("mississippi")
# Counter({'s': 4, 'i': 4, 'p': 2, 'm': 1})
c.most_common(2) # [('s', 4), ('i', 4)]
words = "the cat sat on the mat".split()
Counter(words) # Counter({'the': 2, 'cat': 1, ...})
42. pathlib basics
Show answer
from pathlib import Pathp = Path("/var/log/syslog")
p.name # "syslog"
p.parent # Path("/var/log")
p.suffix # "" (no extension)
p.exists() # True/False
p.read_text() # file contents as string
# Build paths with /
conf = Path.home() / ".config" / "myapp"
conf.mkdir(parents=True, exist_ok=True)
# Glob
for f in Path("/var/log").glob("*.log"):
print(f)
43. subprocess basics
Show answer
import subprocess# Run a command, capture output
result = subprocess.run(
["ls", "-la"],
capture_output=True, text=True, check=True,
)
print(result.stdout)
# NEVER use shell=True with user input
# Always pass args as a list
44. JSON and YAML
Show answer
import json# JSON
data = {"name": "alice", "age": 30}
json.dumps(data) # string
json.dumps(data, indent=2) # pretty
json.loads('{"x": 1}') # dict
with open("f.json") as f:
data = json.load(f) # from file
# YAML (pip install pyyaml)
import yaml
yaml.safe_load(open("f.yaml"))
yaml.dump(data, default_flow_style=False)
45. datetime
Show answer
from datetime import datetime, timedeltanow = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
dt = datetime.strptime("2026-04-11", "%Y-%m-%d")
tomorrow = now + timedelta(days=1)
delta = datetime(2026, 12, 31) - now
print(f"{delta.days} days left")
46. os.environ
Show answer
import oshome = os.environ["HOME"] # raises KeyError if missing
db = os.environ.get("DB_HOST", "localhost") # default if missing
os.environ["MY_VAR"] = "value" # set for this process
# List all env vars starting with AWS_
aws = {k: v for k, v in os.environ.items() if k.startswith("AWS_")}
47. shutil
Show answer
import shutilshutil.copy2("a.txt", "b.txt") # cp -p (preserves metadata)
shutil.copytree("src/", "dst/") # cp -r
shutil.move("old.txt", "new.txt") # mv
shutil.rmtree("/tmp/build") # rm -rf
shutil.which("python3") # /usr/bin/python3
usage = shutil.disk_usage("/")
print(f"{usage.free // 1e9:.0f} GB free")
48. tempfile
Show answer
import tempfile# Temp file (auto-deleted)
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt") as f:
f.write("data")
print(f.name) # /tmp/tmpXXXX.txt
# Temp directory (auto-deleted)
with tempfile.TemporaryDirectory() as d:
print(d) # /tmp/tmpXXXXXX
49. secrets (secure random)
Show answer
import secrets# Secure token
tok = secrets.token_hex(16) # "a3f2...d1b7" (32 hex chars)
tok = secrets.token_urlsafe(16) # URL-safe base64
# Secure random integer
n = secrets.randbelow(100) # 0-99
# Secure password
import string
alpha = string.ascii_letters + string.digits
pwd = "".join(secrets.choice(alpha) for _ in range(16))
Use secrets, not random, for anything security-related.
50. hashlib
Show answer
import hashlibh = hashlib.sha256(b"hello world").hexdigest()
# "b94d27b9934d3e08a52e52d7da7dabfa..."
# Hash a file
def file_hash(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
51. Augmented assignment
Show answer
x = 10x += 5 # 15
x -= 3 # 12
x *= 2 # 24
x //= 5 # 4
x **= 3 # 64
x %= 10 # 4
Also works with strings: s = "hi"; s += "!" → "hi!"
52. Truthy and falsy values
Show answer
# Falsy values:bool(0) # False
bool(0.0) # False
bool("") # False
bool([]) # False
bool({}) # False
bool(None) # False
bool(False) # False
# Everything else is truthy:
bool(1) # True
bool("hi") # True
bool([0]) # True (non-empty list)
# So you can write:
if my_list: # instead of if len(my_list) > 0
53. Multi-line strings and docstrings
Show answer
# Triple quotes for multi-linequery = """
SELECT *
FROM users
WHERE active = true
"""
# Docstrings
def add(a, b):
"""Return the sum of a and b."""
return a + b
print(add.__doc__) # "Return the sum of a and b."
54. frozenset
Show answer
a = frozenset([1, 2, 3])# a.add(4) → AttributeError (immutable)
# Can use as dict key or set element
cache = {frozenset([1,2]): "result"}
# Same set operations work
a | frozenset([3, 4]) # frozenset({1, 2, 3, 4})
55. zip tricks
Show answer
# Transpose a matrixmatrix = [(1,2,3), (4,5,6)]
list(zip(*matrix)) # [(1,4), (2,5), (3,6)]
# Unzip
pairs = [("a",1), ("b",2), ("c",3)]
keys, vals = zip(*pairs)
# keys = ('a','b','c'), vals = (1,2,3)
🟡 Medium (37)¶
1. yield (generators)
Show answer
def countdown(n):while n > 0:
yield n
n -= 1
for x in countdown(3):
print(x) # 3 2 1
Pauses the function and produces a value. Resumes on next iteration. Memory-efficient for large sequences.
2. try / except / else / finally
Show answer
try:f = open("data.txt")
except FileNotFoundError:
print("missing")
else:
print(f.read()) # runs only if no exception
finally:
print("always runs") # cleanup goes here
3. global / nonlocal
Show answer
count = 0def increment():
global count
count += 1
def outer():
x = 10
def inner():
nonlocal x
x += 1
inner()
print(x) # 11
global refers to module scope; nonlocal refers to enclosing function scope.
4. Unpacking ( and *)
Show answer
# Iterable unpackingfirst, *rest = [1, 2, 3, 4] # first=1, rest=[2,3,4]
a, _, b = (1, 2, 3) # ignore middle value
# Function argument unpacking
def f(a, b, c): return a + b + c
args = [1, 2, 3]
f(*args) # 6
kw = {"a": 1, "b": 2, "c": 3}
f(**kw) # 6
# Merge dicts
merged = {**d1, **d2}
5. Walrus operator (:=)
Show answer
# Assign and test in one expressionif (n := len("hello")) > 3:
print(f"long: {n} chars") # long: 5 chars
# In a while loop
while (line := input("> ")) != "quit":
print(f"you said: {line}")
# In a comprehension
results = [y for x in data if (y := expensive(x)) > threshold]
Python 3.8+. Use sparingly — readability first.
6. match / case (structural pattern matching)
Show answer
def handle(command):match command.split():
case ["quit"]:
return "bye"
case ["go", direction]:
return f"going {direction}"
case ["pick", "up", item]:
return f"picked up {item}"
case _:
return "unknown"
handle("go north") # "going north"
handle("pick up key") # "picked up key"
Python 3.10+. Matches structure, not just values.
7. Decorator
Show answer
def log_calls(fn):def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(1, 2) # prints "calling add", returns 3
@decorator is syntactic sugar for: add = log_calls(add)
8. Closure
Show answer
def make_counter(start=0):count = start
def increment():
nonlocal count
count += 1
return count
return increment
c = make_counter()
c() # 1
c() # 2
c() # 3
The inner function captures variables from the enclosing scope. They persist after the outer function returns.
9. args and *kwargs
Show answer
def f(*args, **kwargs):print(args) # tuple of positional args
print(kwargs) # dict of keyword args
f(1, 2, x=3, y=4)
# (1, 2)
# {'x': 3, 'y': 4}
# Keyword-only args (after *):
def g(a, b, *, verbose=False):
pass # verbose must be passed by name
10. Type hints
Show answer
def greet(name: str, times: int = 1) -> str:return (f"hello {name}\
") * times
from typing import Optional
def find(lst: list[int], target: int) -> Optional[int]:
return target if target in lst else None
Hints are not enforced at runtime. Use mypy for static checking. Python 3.9+ allows list[int] instead of List[int].
11. dataclass
Show answer
from dataclasses import dataclass@dataclass
class Point:
x: float
y: float
p = Point(1.0, 2.0)
print(p) # Point(x=1.0, y=2.0)
p == Point(1.0, 2.0) # True (auto __eq__)
Auto-generates __init__, __repr__, __eq__. Use frozen=True for immutable.
12. NamedTuple
Show answer
from typing import NamedTupleclass Point(NamedTuple):
x: float
y: float
p = Point(1.0, 2.0)
p.x # 1.0 (attribute access)
x, y = p # unpacking
p[0] # 1.0 (index access)
Immutable, lightweight, hashable. Great for simple records.
13. Generator expression
Show answer
# Parentheses instead of brackets → lazy evaluationtotal = sum(x**2 for x in range(1000000)) # no list in memory
lines = (line.strip() for line in open("big.log"))
for line in lines:
if "ERROR" in line:
print(line)
14. Custom context manager
Show answer
from contextlib import contextmanager@contextmanager
def timer():
import time
start = time.time()
yield # code inside 'with' runs here
print(f"elapsed: {time.time() - start:.2f}s")
with timer():
sum(range(10**7))
# elapsed: 0.23s
15. @property
Show answer
class Circle:def __init__(self, radius):
self._radius = radius
@property
def area(self):
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.area) # 78.53975 — no parentheses
Makes a method look like an attribute. Add @area.setter for write access.
16. @staticmethod / @classmethod
Show answer
class Date:def __init__(self, y, m, d):
self.y, self.m, self.d = y, m, d
@classmethod
def from_str(cls, s):
return cls(*map(int, s.split("-")))
@staticmethod
def is_valid(s):
return len(s.split("-")) == 3
Date.from_str("2026-04-11") # creates Date
Date.is_valid("2026-04-11") # True
17. len and getitem
Show answer
class Deck:def __init__(self):
self.cards = list(range(52))
def __len__(self):
return len(self.cards)
def __getitem__(self, i):
return self.cards[i]
d = Deck()
len(d) # 52
d[0] # 0
d[-1] # 51
d[2:5] # [2, 3, 4] — slicing works too
18. enter and exit
Show answer
class TempDir:def __enter__(self):
import tempfile
self.path = tempfile.mkdtemp()
return self.path
def __exit__(self, exc_type, exc_val, exc_tb):
import shutil
shutil.rmtree(self.path)
return False # don't suppress exceptions
with TempDir() as d:
print(d) # /tmp/xyz — auto-cleaned up after
19. add, eq, lt (operator overloading)
Show answer
class Vec:def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Vec(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
Vec(1,2) + Vec(3,4) # Vec(4, 6)
Vec(1,2) == Vec(1,2) # True
20. super()
Show answer
class Base:def __init__(self, x):
self.x = x
class Child(Base):
def __init__(self, x, y):
super().__init__(x)
self.y = y
c = Child(1, 2)
print(c.x, c.y) # 1 2
Delegates to the next class in the MRO. Always use super() in __init__ for cooperative inheritance.
21. async / await
Show answer
import asyncioasync def fetch(url):
await asyncio.sleep(1) # simulate I/O
return f"data from {url}"
async def main():
results = await asyncio.gather(
fetch("a.com"),
fetch("b.com"),
)
print(results) # both finish in ~1s, not 2s
asyncio.run(main())
For I/O-bound concurrency. Not parallelism — use multiprocessing for CPU-bound work.
22. Exception hierarchy
Show answer
# Common built-in exceptions:# BaseException
# ├── KeyboardInterrupt
# ├── SystemExit
# └── Exception
# ├── ValueError
# ├── TypeError
# ├── KeyError
# ├── IndexError
# ├── FileNotFoundError
# ├── PermissionError
# ├── AttributeError
# └── RuntimeError
# Custom exceptions:
class AppError(Exception):
pass
class NotFoundError(AppError):
pass
23. Custom exceptions
Show answer
class ValidationError(Exception):def __init__(self, field, message):
self.field = field
super().__init__(f"{field}: {message}")
try:
raise ValidationError("email", "invalid format")
except ValidationError as e:
print(e) # email: invalid format
print(e.field) # email
24. Nested comprehension
Show answer
matrix = [[1,2,3], [4,5,6], [7,8,9]]# Flatten
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Transpose
transpose = [[row[i] for row in matrix] for i in range(3)]
# [[1,4,7], [2,5,8], [3,6,9]]
25. collections.deque
Show answer
from collections import dequeq = deque([1, 2, 3])
q.append(4) # right: [1, 2, 3, 4]
q.appendleft(0) # left: [0, 1, 2, 3, 4]
q.pop() # 4
q.popleft() # 0
# Fixed-size buffer (keeps last N)
buf = deque(maxlen=3)
for i in range(5):
buf.append(i)
print(buf) # deque([2, 3, 4])
26. itertools essentials
Show answer
import itertools# chain — flatten iterables
list(itertools.chain([1,2], [3,4])) # [1, 2, 3, 4]
# islice — lazy head/tail
list(itertools.islice(range(100), 5)) # [0, 1, 2, 3, 4]
# groupby (must be sorted first)
from itertools import groupby
data = sorted(["aa", "ab", "ba", "bb"], key=lambda x: x[0])
for k, g in groupby(data, key=lambda x: x[0]):
print(k, list(g)) # a ['aa','ab'], b ['ba','bb']
# product — nested loops
list(itertools.product("AB", "12")) # [('A','1'),('A','2'),('B','1'),('B','2')]
27. functools essentials
Show answer
from functools import lru_cache, partial, reduce# lru_cache — memoization
@lru_cache(maxsize=128)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
fib(100) # instant
# partial — fix some arguments
from functools import partial
int_from_hex = partial(int, base=16)
int_from_hex("ff") # 255
# reduce — fold a sequence
reduce(lambda a, b: a * b, [1,2,3,4]) # 24
28. re (regular expressions)
Show answer
import re# Search
m = re.search(r"(\\d+)", "error on line 42")
m.group(1) # "42"
# Find all
re.findall(r"\\b\\w+@\\w+\\.\\w+\\b", text)
# Sub (replace)
re.sub(r"\\s+", " ", "too many spaces") # "too many spaces"
# Split
re.split(r"[;,]\\s*", "a, b; c") # ['a', 'b', 'c']
# Compile for reuse
pattern = re.compile(r"ERROR: (.+)")
29. logging
Show answer
import logginglogging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger(__name__)
log.debug("details") # hidden (below INFO)
log.info("started") # shown
log.warning("slow") # shown
log.error("failed") # shown
Use logging, not print(), in production code.
30. argparse
Show answer
import argparsep = argparse.ArgumentParser(description="greet user")
p.add_argument("name")
p.add_argument("-n", "--count", type=int, default=1)
p.add_argument("-v", "--verbose", action="store_true")
args = p.parse_args()
for _ in range(args.count):
print(f"hello {args.name}")
# Usage: python greet.py alice -n 3 -v
31. threading
Show answer
from concurrent.futures import ThreadPoolExecutorimport urllib.request
def fetch(url):
return urllib.request.urlopen(url).read()[:100]
urls = ["https://example.com"] * 5
with ThreadPoolExecutor(max_workers=5) as pool:
results = list(pool.map(fetch, urls))
Good for I/O-bound tasks. GIL prevents true CPU parallelism — use ProcessPoolExecutor for that.
32. multiprocessing
Show answer
from concurrent.futures import ProcessPoolExecutordef crunch(n):
return sum(i*i for i in range(n))
with ProcessPoolExecutor(max_workers=4) as pool:
results = list(pool.map(crunch, [10**6]*4))
Bypasses the GIL — true parallelism for CPU-bound work. Each worker is a separate process.
33. Abstract base class (ABC)
Show answer
from abc import ABC, abstractmethodclass Shape(ABC):
@abstractmethod
def area(self) -> float:
...
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r ** 2
# Shape() → TypeError (can't instantiate)
Circle(5).area() # 78.5
34. str vs bytes
Show answer
s = "hello" # str (unicode text)b = b"hello" # bytes (raw bytes)
# Encode: str → bytes
b = s.encode("utf-8") # b'hello'
# Decode: bytes → str
s = b.decode("utf-8") # 'hello'
# You WILL hit this at network/file boundaries
# Always decode bytes to str as early as possible
35. for/else and while/else
Show answer
for n in [2, 3, 5, 7]:if n == 4:
print("found 4")
break
else:
print("4 not found") # ← runs because no break
The else block runs only if the loop completes without break. Useful for search patterns.
36. LEGB scope rule
Show answer
x = "global"def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global
# Python searches: Local → Enclosing → Global → Built-in
# import builtins to see the B layer
37. Class variable vs instance variable
Show answer
class Dog:species = "canine" # class variable (shared)
def __init__(self, name):
self.name = name # instance variable (per-object)
a = Dog("rex")
b = Dog("fido")
Dog.species # "canine"
a.name # "rex"
# Gotcha: mutable class vars are shared!
class Bad:
items = [] # all instances share this list
🔴 Hard (2)¶
1. slots
Show answer
class Point:__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 → AttributeError (no dynamic attrs)
~40% less memory per instance. Use when creating millions of objects.
2. Protocol (structural typing)
Show answer
from typing import Protocolclass Closable(Protocol):
def close(self) -> None: ...
def shutdown(obj: Closable) -> None:
obj.close()
# Any object with a .close() method works
# No inheritance needed — duck typing + type checking
shutdown(open("/dev/null")) # file has .close()