Spaced Repetition Learning
My personal flashcards using the Free Spaced Repetition Scheduler (FSRS) algorithm.
For everyone besides me, the data is locally stored. So nice try resetting my progress!
Which CLI command should you run to diagnose issues with your Doom Emacs installation?
doom doctor
This command checks your environment for common issues, missing dependencies, and configuration errors.
What is the purpose of the doom sync command?
It synchronizes your configuration with the installed packages. You
must run this after modifying init.el or packages.el to install/remove packages and regenerate autoloads.
How do you quickly switch between different projects?
SPC p p
This opens a list of known projects. Once a project is selected, you are prompted to find a file within that specific project.
What is the binding to switch between open buffers?
SPC b b (or SPC ,)
This brings up a list of all currently open files and internal Emacs buffers.
What are the 'Evil' keys for moving the cursor between split windows?
- SPC w h: Move left
- SPC w j: Move down
- SPC w k: Move up
- SPC w l: Move right
Which command opens the 'Capture' interface for quickly adding notes/tasks in Org Mode?
SPC X (uppercase X)
Alternatively, SPC n n if the (org +journal) module is enabled,
but SPC X is the global dispatch for capture templates.
How do you jump to the start and end of a line in Evil mode?
- 0: Absolute start of the line.
- ^: First non-whitespace character.
- $: End of the line.
What is the difference between w, e, and b?
- w: Jump to the start of the next word.
- e: Jump to the end of the current word.
- b: Jump back to the start of the previous word.
How do you jump between matching parentheses, brackets, or braces?
Press % while the cursor is on one of the delimiters.
What is the Doom-specific keybinding to save the current buffer?
SPC f s
Alternatively, the Vim command :w works as well.
When using a language server (LSP), how do you jump to a definition or find references?
- g d: Jump to Definition.
- g D: Jump to References.
- g h: Show documentation/hover info.
How do you quickly switch between workspace numbers (1-9)?
M-1 through M-9 (Alt + number).
This allows you to treat Emacs workspaces like browser tabs or virtual desktops.
What is the difference between pressing i and a?
- i (insert): Enters insert mode before the cursor.
- a (append): Enters insert mode after the cursor.
How do you insert a new line while remaining in Normal mode?
- o: Opens a new line below the cursor and enters Insert mode.
- O: Opens a new line above the cursor and enters Insert mode.
What are the three types of Visual Selection modes?
- v: Visual Character (standard selection).
- V: Visual Line (selects whole lines).
- C-v (Ctrl+v): Visual Block (selects rectangular columns).
How do you delete everything from the cursor to the end of the line?
D (shift+d)
This is equivalent to d$.
What does the cc (or S) command do?
It clears the entire current line and places you in Insert mode with the correct indentation.
What are the Evil keys for Copy and Paste?
- y (yank): Copy selected text.
- p (put): Paste after the cursor/line.
- P: Paste before the cursor/line.
How do you replace a single character without leaving Normal mode?
Press r followed by the character you want.
Example: r x replaces the character under the cursor with
'x'.
defmodule Greeter do
# A lower-arity function delegates to a higher-arity one with a default value
def greet(name), do: greet(name, "Hello")
def greet(name, greeting), do: "#{greeting}, #{name}!"
end
# Examples
Greeter.greet("Sam")
# => "Hello, Sam!"
Greeter.greet("Sam", "Hi")
# => "Hi, Sam!" あ
Romaji: a
い
Romaji: i
う
Romaji: u
え
Romaji: e
お
Romaji: o
か
Romaji: ka
き
Romaji: ki
く
Romaji: ku
け
Romaji: ke
こ
Romaji: ko
さ
Romaji: sa
し
Romaji: shi
す
Romaji: su
せ
Romaji: se
そ
Romaji: so
た
Romaji: ta
ち
Romaji: chi
つ
Romaji: tsu
て
Romaji: te
と
Romaji: to
な
Romaji: na
に
Romaji: ni
ぬ
Romaji: nu
ね
Romaji: ne
の
Romaji: no
は
Romaji: ha (pronounced "wa" when used as the topic particle)
ひ
Romaji: hi
ふ
Romaji: fu (sometimes written "hu")
へ
Romaji: he (pronounced "e" when used as particle)
ほ
Romaji: ho
ま
Romaji: ma
み
Romaji: mi
む
Romaji: mu
め
Romaji: me
も
Romaji: mo
や
Romaji: ya
ゆ
Romaji: yu
よ
Romaji: yo
ら
Romaji: ra
り
Romaji: ri
る
Romaji: ru
れ
Romaji: re
ろ
Romaji: ro
わ
Romaji: wa
を
Romaji: wo (pronounced 'o' as a particle)
ん
Romaji: n
When did World War II begin and end?
Begin: September 1, 1939 (Germany invades Poland)
End: September 2, 1945 (Japan's formal surrender)
how do you define a function in JavaScript?
function hello_world() {
console.log("Hello, world!");
} What is the difference between let and var in JavaScript
Let was introduced in ES6 and is block-scoped, because the scooping rules of var are quite confusing.
By default you should use let.
Variables declared with var keyword are hoisted and initialized which means they are accessible in their enclosing scope even before they are declared, however their value is undefined before the declaration statement is reached.
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
} What is a closure in JavaScript?
Explain with an example.
A closure is a function that has access to variables in its outer (enclosing) lexical scope, even after the outer function has returned.
function outer() {
let count = 0;
return function inner() {
count++;
return count;
}
}
What is the difference between var, let,
and const?
- var: Function-scoped, can be redeclared, hoisted
- let: Block-scoped, cannot be redeclared, not hoisted
- const: Block-scoped, cannot be redeclared or reassigned, not hoisted
What are the two fundamental datastructures that can be used to create any other?
- Linked-Lists: Dynamic size, efficient insertions/deletions
- Arrays: Fixed size, efficient random access.
Explain what is dynamic programming for a technique?
You break down a complex problem into simpler subproblems, solve each subproblem just once, and often store their solutions as they are used in subsequent computations.
Another characteristic of dynamic programming is the usage of recursion.
The best example of dynamic programming is calculating Fibonacci numbers.
function fibonacci(n, memo = {}) {
if (n <= 1) return n;
if (memo[n]) return memo[n];
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
return memo[n];
}
What is a void* (void pointer) and what are the two strict
restrictions on its usage?
A void pointer is a generic pointer that can hold the address of any data type, but has no type information itself.
The Two Restrictions:
- No Dereferencing: You cannot use
*ptrbecause the compiler doesn't know the data size (1 byte? 4 bytes?). - No Arithmetic: You cannot use
ptr++because the compiler doesn't know how many bytes to jump.
You must explicitly cast it to a specific type before doing either.
int a = 10;
void* ptr = &a;
// COMPILER ERROR
// printf("%d", *ptr);
// CORRECT (Cast first)
printf("%d", *(int*)ptr);
What are the two fundamental pointer operators, & and *, and what does each one do?
They are the inverse of each other:
-
&(Address-of): A unary operator that gets the memory address of a variable. -
*(Dereference): A unary operator that goes to a memory address at a pointer and gets the value stored there.
int value = 42;
// Use & to get the address of 'value'
int* pointer = &value;
// Use * to get the value at 'pointer's address
int retrievedValue = *pointer;
// retrievedValue is now 42 What is the core relationship between pointers and arrays in C?
An array's name acts as a constant pointer to its first element.
arris equivalent to&arr[0].-
Array access
arr[i]is just "syntactic sugar" for pointer arithmetic:*(arr + i). - When you pass an array to a function, you are actually just passing a pointer to its first element.
int nums[] = {10, 20, 30};
int* ptr = nums; // 'nums' decays to a pointer
// These two lines are 100% equivalent
printf("%d", nums[1]); // Prints 20
printf("%d", *(ptr + 1)); // Prints 20
What is a NULL pointer, and why is it crucial for safe programming?
NULL is a special macro (usually (void*)0)
that represents a pointer that "points to nothing." It's a
guaranteed-invalid address.
Why it's crucial:
- Safety Check: You must always check
for
NULLbefore dereferencing a pointer that *might* be invalid, especially after a call tomalloc. - Error-Handling: Functions like
mallocreturnNULLto signal a failure (e.g., out of memory).
int* ptr = (int*)malloc(sizeof(int) * 100);
// CRITICAL: Check for NULL before using the pointer.
if (ptr == NULL) {
printf("Error: Out of memory!\n");
// Handle error...
} else {
// It's safe to use ptr
ptr[0] = 5;
} What is the difference between Single Dispatch and Multiple Dispatch?
The difference lies in how many objects' runtime types are used to select which method implementation to execute.
- Single Dispatch (Java, C++, Python): The decision
is based on the runtime type of the receiver only (the object
before the dot).
x.f(y)selects implementation based onx. - Multiple Dispatch (Julia, Common Lisp): The decision
is based on the runtime types of all arguments involved.
f(x, y)selects implementation based on the combination ofxANDy.
What specific problem does Multiple Dispatch solve (often illustrated by the "Collision Problem")?
It solves the difficulty of defining operations that depend on the concrete types of two or more interacting objects without using messy conditional logic.
Example: A game engine handling collisions.
-
collide(Asteroid, Spaceship)-> Ship takes damage. collide(Asteroid, Asteroid)-> Both bounce.
In Single Dispatch, asteroid.collide(obj) only knows it hit an "Object". You are forced to use instanceof checks or the Visitor Pattern (Double Dispatch) to
determine what obj actually is.
Multiple Dispatch handles this natively by selecting the exact method signature matching both types.
How do you add fields to a python class?
class Person():
def __init__(self, name):
self.name = name How do you format strings in Python?
f'Hello, {friends_name}' How do you add methods to a python class?
class Person():
def SayHello(self, name):
print(f'Hello, my friend: {name})' How do you create a new python project using UV?
uv init PROJECT_NAME How do add new dependencies to UV?
uv add LIBRARY_NAME Imagine this folder structure. How do you import the person class into your main.py?
project/
main.py
LIBARY/
__init__.py
classes.py
LIBARY/__init__.py:
from.classes import Person from LIBARY import Person What is the difference between mutable and immutable types in Python? Give examples of each.
- Immutable (cannot be changed after creation):
int,float,str,tuple,frozenset,bool - Mutable (can be changed in-place):
list,dict,set,bytearray
This matters because mutable default arguments in functions are a common bug source:
# BUG: The default list is shared across all calls!
def add_item(item, lst=[]):
lst.append(item)
return lst
# CORRECT: Use None as default
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst How do you create, access, and modify a dictionary in Python?
# Create
person = {"name": "Alice", "age": 30}
# Access
person["name"] # "Alice" (KeyError if missing)
person.get("name") # "Alice" (None if missing)
person.get("job", "N/A") # "N/A" (custom default)
# Modify
person["age"] = 31
person["job"] = "Dev" # adds new key
# Useful methods
person.keys() # dict_keys(["name", "age", "job"])
person.values() # dict_values(["Alice", 31, "Dev"])
person.items() # dict_items([("name","Alice"), ...])
person.pop("job") # removes and returns "Dev" How does slicing work in Python? What does sequence[start:stop:step] mean?
Slicing extracts a portion of a sequence. The syntax is [start:stop:step] where start is inclusive, stop is exclusive, and step is the increment.
nums = [0, 1, 2, 3, 4, 5, 6, 7]
nums[2:5] # [2, 3, 4]
nums[:3] # [0, 1, 2] (from start)
nums[5:] # [5, 6, 7] (to end)
nums[::2] # [0, 2, 4, 6] (every 2nd)
nums[::-1] # [7, 6, 5, 4, 3, 2, 1, 0] (reversed)
nums[-3:] # [5, 6, 7] (last 3)
# Also works on strings
"Hello"[1:4] # "ell" What are list comprehensions in Python and how do you use them?
A concise way to create lists by transforming and/or filtering elements from an iterable in a single expression.
# Basic: [expression for item in iterable]
squares = [x**2 for x in range(5)]
# [0, 1, 4, 9, 16]
# With filter: [expression for item in iterable if condition]
evens = [x for x in range(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]
# With transform + filter
names = ["alice", "BOB", "Charlie"]
upper_long = [n.upper() for n in names if len(n) > 3]
# ["ALICE", "CHARLIE"]
# Dict comprehension
{k: v**2 for k, v in {"a": 1, "b": 2}.items()}
# {"a": 1, "b": 4} What is a generator in Python and how does yield work?
A generator is a function that produces a sequence of values lazily (one at a time) using yield instead of return. It pauses execution and resumes where it left off on the next call.
def count_up(n):
i = 0
while i < n:
yield i # pauses here, returns i
i += 1 # resumes here on next()
gen = count_up(3)
next(gen) # 0
next(gen) # 1
next(gen) # 2
# Most common usage: in a for loop
for num in count_up(1000000):
print(num) # Memory-efficient: only 1 value in memory at a time How does exception handling work in Python with try/except?
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}")
except (TypeError, ValueError):
print("Type or value error")
except Exception as e:
print(f"Unexpected: {e}") # catches all other exceptions
else:
print("No errors occurred") # runs only if no exception
finally:
print("Always runs") # cleanup, runs no matter what
# Raising exceptions
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b What are *args and **kwargs in Python?
They let a function accept a variable number of arguments.
*argscollects extra positional arguments into a tuple.**kwargscollects extra keyword arguments into a dict.
def example(required, *args, **kwargs):
print(required) # "hello"
print(args) # (1, 2, 3)
print(kwargs) # {"name": "Alice", "age": 30}
example("hello", 1, 2, 3, name="Alice", age=30)
# Also used for unpacking when calling:
def add(a, b, c):
return a + b + c
nums = [1, 2, 3]
add(*nums) # unpacks list -> add(1, 2, 3) What are lambda functions in Python?
Anonymous, single-expression functions defined inline with the lambda keyword. Useful for short operations passed as arguments.
# Syntax: lambda arguments: expression
square = lambda x: x ** 2
square(5) # 25
# Common use: sorting with a key
users = [("Bob", 25), ("Alice", 30), ("Charlie", 20)]
sorted(users, key=lambda u: u[1])
# [("Charlie", 20), ("Bob", 25), ("Alice", 30)]
# Common use: filtering
nums = [1, 2, 3, 4, 5, 6]
list(filter(lambda x: x % 2 == 0, nums))
# [2, 4, 6] What are decorators in Python and how do they work?
A decorator is a function that wraps another function to extend its behavior without modifying it. The @decorator syntax is shorthand for func = decorator(func).
import functools
def log_calls(func):
@functools.wraps(func) # preserves original name/docstring
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"Returned {result}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
# Calling add
# Returned 5 How do enumerate() and zip() work in Python?
enumerate() gives you both the index and the value while iterating.
zip() combines multiple iterables element-by-element into tuples.
# enumerate: get index + value
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
# zip: pair up elements from multiple iterables
names = ["Alice", "Bob"]
scores = [95, 87]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Alice: 95
# Bob: 87
# zip stops at the shortest iterable
dict(zip(names, scores)) # {"Alice": 95, "Bob": 87} How does tuple unpacking (destructuring) work in Python?
# Basic unpacking
x, y, z = (1, 2, 3)
# Swap variables (no temp needed!)
a, b = b, a
# Star expression: capture the rest
first, *middle, last = [1, 2, 3, 4, 5]
# first=1, middle=[2, 3, 4], last=5
# Ignore values with _
name, _, age = ("Alice", "unused", 30)
# Nested unpacking
(a, b), (c, d) = (1, 2), (3, 4)
# In for loops
pairs = [(1, "a"), (2, "b")]
for num, letter in pairs:
print(f"{num} -> {letter}") What is a context manager and how does the with statement work?
A context manager guarantees setup and cleanup actions, even if an error occurs. The with statement calls __enter__ on entry and __exit__ on exit.
# Most common: file handling
# File is automatically closed, even if an error occurs
with open("data.txt", "r") as f:
content = f.read()
# f is closed here
# Without 'with' you'd need:
f = open("data.txt", "r")
try:
content = f.read()
finally:
f.close()
# Multiple context managers
with open("in.txt") as src, open("out.txt", "w") as dst:
dst.write(src.read()) How does class inheritance work in Python?
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def __init__(self, name, indoor=True):
super().__init__(name) # call parent constructor
self.indoor = indoor
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Rex")
dog.speak() # "Rex says Woof!"
isinstance(dog, Animal) # True What are dunder (magic) methods in Python? Name the most important ones.
Dunder methods (double underscore, e.g. __init__) let you define how objects behave with built-in operations and functions.
class Vector:
def __init__(self, x, y): # constructor
self.x = x
self.y = y
def __repr__(self): # repr(obj), debugging
return f"Vector({self.x}, {self.y})"
def __str__(self): # str(obj), print(obj)
return f"({self.x}, {self.y})"
def __len__(self): # len(obj)
return 2
def __eq__(self, other): # obj == other
return self.x == other.x and self.y == other.y
def __add__(self, other): # obj + other
return Vector(self.x + other.x, self.y + other.y)
v = Vector(1, 2) + Vector(3, 4) # Vector(4, 6) How do type hints work in Python?
Type hints are optional annotations that document expected types. Python does not enforce them at runtime — use tools like mypy for static checking.
# Variables
name: str = "Alice"
age: int = 30
# Functions
def greet(name: str, times: int = 1) -> str:
return f"Hello, {name}! " * times
# Collections (Python 3.9+)
scores: list[int] = [90, 85, 92]
lookup: dict[str, int] = {"alice": 95}
# Optional values
from typing import Optional
def find(name: str) -> Optional[str]:
... # returns str or None
# Union types (Python 3.10+)
def process(value: int | str) -> None:
... What are the most important string methods in Python?
s = " Hello, World! "
s.strip() # "Hello, World!" (removes whitespace)
s.lower() # " hello, world! "
s.upper() # " HELLO, WORLD! "
s.replace("World", "Python") # " Hello, Python! "
s.split(",") # [" Hello", " World! "]
"-".join(["a", "b", "c"]) # "a-b-c"
s.startswith(" He") # True
s.endswith("! ") # True
"World" in s # True (membership test)
s.find("World") # 9 (index, or -1 if not found)
s.count("l") # 3 What values are falsy in Python? What is the difference between == and is?
Falsy values (evaluate to False in a boolean context):
NoneFalse- Zero:
0,0.0,0j - Empty sequences:
"",[],(),,set()
Everything else is truthy.
# == checks VALUE equality
# is checks IDENTITY (same object in memory)
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True (same contents)
a is b # False (different objects)
# Always use 'is' for None checks
x = None
if x is None: # CORRECT
...
if x == None: # WRONG (can be overridden by __eq__)
... What is the LEGB rule for variable scope in Python?
Python resolves variable names by searching four scopes in order:
- Local — inside the current function
- Enclosing — in any enclosing (outer) function
- Global — at the module level
- Built-in — Python's built-in names (
print,len, etc.)
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # "local" (L)
inner()
# To modify outer scope variables:
def counter():
count = 0
def increment():
nonlocal count # refer to enclosing scope
count += 1
increment()
return count # 1 What are the most essential built-in functions in Python?
# Type conversion
int("42") # 42
float("3.14") # 3.14
str(42) # "42"
list("abc") # ["a", "b", "c"]
bool(0) # False
# Math
abs(-5) # 5
max(3, 7, 2) # 7
min(3, 7, 2) # 2
sum([1, 2, 3]) # 6
round(3.14159, 2) # 3.14
# Inspection
type(42) # <class 'int'>
isinstance(42, int) # True
len([1, 2, 3]) # 3
dir(obj) # list all attributes/methods
# Iteration
range(5) # 0, 1, 2, 3, 4
sorted([3,1,2]) # [1, 2, 3]
reversed([1,2]) # iterator: 2, 1
map(str, [1,2]) # iterator: "1", "2"
any([F, T, F]) # True
all([T, T, F]) # False What are dataclasses and why should you use them?
@dataclass automatically generates __init__, __repr__, __eq__, and more from class field definitions. Reduces boilerplate for classes that primarily store data.
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float
label: str = "origin" # default value
p = Point(1.0, 2.0)
print(p) # Point(x=1.0, y=2.0, label='origin')
# Immutable version
@dataclass(frozen=True)
class Color:
r: int
g: int
b: int
red = Color(255, 0, 0)
red.r = 128 # FrozenInstanceError! What is the ternary (conditional) expression and the walrus operator (:=) in Python?
# Ternary expression: value_if_true if condition else value_if_false
status = "adult" if age >= 18 else "minor"
# Walrus operator := (Python 3.8+)
# Assigns AND returns a value in a single expression
# Without walrus:
line = input()
while line != "quit":
print(line)
line = input()
# With walrus:
while (line := input()) != "quit":
print(line)
# Useful in list comprehensions
results = [y for x in data if (y := expensive(x)) > threshold] What does it mean that functions are first-class objects in Python?
Functions can be assigned to variables, passed as arguments, returned from other functions, and stored in data structures — just like any other value.
# Assign to a variable
greet = print
greet("Hello") # Hello
# Store in a data structure
ops = {"+": lambda a, b: a + b,
"-": lambda a, b: a - b}
ops["+"](3, 4) # 7
# Pass as an argument
def apply(func, value):
return func(value)
apply(str.upper, "hello") # "HELLO"
# Return from a function
def multiplier(n):
return lambda x: x * n
double = multiplier(2)
double(5) # 10 What is a higher-order function? Give Python examples.
A function that takes a function as an argument and/or returns a function. This is the foundation of functional programming.
# Takes a function as argument
sorted(["banana", "apple", "cherry"], key=len)
# ["apple", "cherry", "banana"]
# Built-in higher-order functions
list(map(str.upper, ["a", "b"])) # ["A", "B"]
list(filter(str.isalpha, ["a", "1"])) # ["a"]
# Returns a function (decorator pattern)
def logged(func):
def wrapper(*args):
print(f"Calling {func.__name__}")
return func(*args)
return wrapper
# Custom higher-order function
def repeat(n):
"""Returns a function that repeats its input n times."""
def decorator(func):
def wrapper(*args):
return [func(*args) for _ in range(n)]
return wrapper
return decorator What is a pure function and why does it matter in functional programming?
A pure function has two properties:
- Deterministic: Same inputs always produce the same output.
- No side effects: Does not modify external state (no mutating globals, I/O, etc.).
Benefits: Easier to test, debug, reason about, and parallelize.
# PURE — no side effects, same input -> same output
def add(a, b):
return a + b
# IMPURE — modifies external state
total = 0
def add_to_total(x):
global total
total += x # side effect: mutates global
return total
# IMPURE — depends on external state
import random
def roll():
return random.randint(1, 6) # non-deterministic
# FP style: prefer transforming data over mutating it
nums = [3, 1, 2]
sorted_nums = sorted(nums) # pure: returns new list
nums.sort() # impure: mutates in place How do map(), filter(), and reduce() work in Python?
The three classic FP operations for transforming sequences:
from functools import reduce
nums = [1, 2, 3, 4, 5]
# map: apply a function to every element
list(map(lambda x: x ** 2, nums))
# [1, 4, 9, 16, 25]
# filter: keep elements where function returns True
list(filter(lambda x: x % 2 == 0, nums))
# [2, 4]
# reduce: fold list into a single value
# reduce(func, iterable, initial)
reduce(lambda acc, x: acc + x, nums, 0)
# 15 (((((0+1)+2)+3)+4)+5)
reduce(lambda acc, x: acc * x, nums, 1)
# 120 (1*2*3*4*5)
# Pythonic alternatives (often preferred):
[x ** 2 for x in nums] # instead of map
[x for x in nums if x % 2 == 0] # instead of filter
sum(nums) # instead of reduce for sums What is a closure in Python and how is it used in functional programming?
A closure is a function that captures and remembers variables from its enclosing scope, even after that scope has finished executing. This lets you create functions with "baked-in" state.
# A closure "closes over" the variable 'factor'
def make_multiplier(factor):
def multiply(x):
return x * factor # 'factor' is captured
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
double(5) # 10
triple(5) # 15
# Practical: configurable validators
def make_range_checker(low, high):
def check(value):
return low <= value <= high
return check
is_valid_age = make_range_checker(0, 150)
is_valid_age(25) # True
is_valid_age(200) # False What are the most useful functions in Python's functools module?
import functools
# partial: "freeze" some arguments of a function
def power(base, exp):
return base ** exp
square = functools.partial(power, exp=2)
cube = functools.partial(power, exp=3)
square(5) # 25
cube(3) # 27
# lru_cache: memoize function results (pure functions only!)
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(100) # instant, not exponential
# reduce: fold a sequence into a single value
functools.reduce(lambda a, b: a + b, [1, 2, 3]) # 6
# wraps: preserve metadata when writing decorators
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper What are the most useful functions in Python's itertools module?
itertools provides memory-efficient building blocks for working with iterators in a functional style.
import itertools
# chain: flatten multiple iterables into one
list(itertools.chain([1, 2], [3, 4], [5]))
# [1, 2, 3, 4, 5]
# islice: lazy slicing of any iterator
list(itertools.islice(range(100), 5, 10))
# [5, 6, 7, 8, 9]
# product: cartesian product (nested loops)
list(itertools.product("AB", [1, 2]))
# [("A",1), ("A",2), ("B",1), ("B",2)]
# combinations & permutations
list(itertools.combinations("ABC", 2))
# [("A","B"), ("A","C"), ("B","C")]
list(itertools.permutations("AB", 2))
# [("A","B"), ("B","A")]
# groupby: group consecutive elements by a key
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# a [("a",1), ("a",2)]
# b [("b",3)]
# starmap: like map() but unpacks argument tuples
list(itertools.starmap(pow, [(2, 3), (3, 2)]))
# [8, 9] How do you compose functions in Python (applying multiple transformations in sequence)?
Python has no built-in compose operator, but you can build pipelines using reduce or simple helper functions.
from functools import reduce
# Manual composition
def compose(*funcs):
"""Apply functions right-to-left: compose(f, g)(x) = f(g(x))"""
return reduce(lambda f, g: lambda *a: f(g(*a)), funcs)
shout = compose(str.upper, str.strip)
shout(" hello ") # "HELLO"
# Pipeline style (left-to-right, more readable)
def pipe(*funcs):
"""Apply functions left-to-right: pipe(f, g)(x) = g(f(x))"""
return reduce(lambda f, g: lambda *a: g(f(*a)), funcs)
process = pipe(str.strip, str.upper, lambda s: s + "!")
process(" hello ") # "HELLO!"
# Practical: data processing pipeline
pipeline = pipe(
lambda data: [x for x in data if x > 0], # filter
lambda data: [x ** 2 for x in data], # transform
sum # aggregate
)
pipeline([-1, 2, -3, 4, 5]) # 4+16+25 = 45 How do you achieve immutability in Python for functional programming?
FP favors immutable data — transforming values into new values rather than mutating existing ones.
# Use tuples instead of lists
point = (3, 4) # immutable
# point[0] = 5 # TypeError!
# Use frozenset instead of set
allowed = frozenset({"read", "write"})
# Use frozen dataclasses
from dataclasses import dataclass
@dataclass(frozen=True)
class User:
name: str
age: int
u = User("Alice", 30)
# u.age = 31 # FrozenInstanceError!
# FP pattern: return new objects instead of mutating
def birthday(user: User) -> User:
return User(user.name, user.age + 1) # new object
# NamedTuple: lightweight immutable data
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
p = Point(1.0, 2.0)
p2 = Point(p.x + 1, p.y) # create new, don't mutate What is the operator module and why is it useful in functional Python?
The operator module provides function equivalents of Python operators and attribute/item accessors — cleaner and faster than writing lambdas.
from operator import add, mul, itemgetter, attrgetter
from functools import reduce
# Replace lambdas with named functions
reduce(add, [1, 2, 3, 4]) # 10 (instead of lambda a,b: a+b)
reduce(mul, [1, 2, 3, 4]) # 24 (instead of lambda a,b: a*b)
# itemgetter: access items by index/key
users = [{"name": "Bob", "age": 25},
{"name": "Alice", "age": 30}]
sorted(users, key=itemgetter("age"))
# [{"name":"Bob",...}, {"name":"Alice",...}]
# vs lambda: sorted(users, key=lambda u: u["age"])
# attrgetter: access object attributes
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
points = [Point(3, 1), Point(1, 2)]
sorted(points, key=attrgetter("x"))
# [Point(x=1, y=2), Point(x=3, y=1)] When should you use comprehensions vs map()/filter() in Python?
Comprehensions are the Pythonic default. Use map/filter when passing an existing named function.
names = ["alice", "bob", "charlie"]
# PREFER comprehension with inline logic
[n.upper() for n in names if len(n) > 3]
# ["ALICE", "CHARLIE"]
# PREFER map/filter with existing functions
list(map(str.upper, names))
# ["ALICE", "BOB", "CHARLIE"]
# Avoid: lambda + map is worse than a comprehension
list(map(lambda x: x ** 2, range(5))) # awkward
[x ** 2 for x in range(5)] # cleaner
# Generator expressions for lazy evaluation
# (round brackets instead of square)
sum(x ** 2 for x in range(1000000)) # memory-efficient
# Nested comprehensions (use sparingly)
matrix = [[1, 2], [3, 4], [5, 6]]
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6] What are generator expressions and how do they differ from list comprehensions?
Generator expressions use parentheses instead of brackets and produce values lazily — one at a time, without building the entire list in memory.
# List comprehension: builds entire list in memory
squares_list = [x**2 for x in range(1000000)] # ~8MB
# Generator expression: produces values on demand
squares_gen = (x**2 for x in range(1000000)) # ~0MB
# Use directly in functions that consume iterables
sum(x**2 for x in range(1000000)) # no extra brackets needed
max(len(w) for w in words)
any(x > 100 for x in data)
# Chaining generators for pipeline processing
import os
files = (f for f in os.listdir(".") if f.endswith(".py"))
sizes = (os.path.getsize(f) for f in files)
total = sum(sizes) # processes one file at a time How do you create a table using SQL?
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username TEXT
); CREATE TABLE table_name (
column1 datatype,
column2 datatype,
column3 datatype,
....
); What is the difference of INTEGER and INT in SQLite when using them as a primary key?
When we use INTERGER in a primary key it will auto increment whereas INT will not. This is because INTEGER PRIMARY KEY is the special alias for ROWID in SQLite.
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username TEXT
); Here the entries in the "id" column will NOT auto increment.
CREATE TABLE users_bad (
id INT PRIMARY KEY,
username TEXT
); How do you add a new column to an existing table in SQL?
ALTER TABLE table_name
ADD column_name datatype; How do you insert a new row into a table?
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...); How do you insert a multiple new rows into a table?
INSERT INTO table_name (column1, column2, column3)
VALUES
(value1, value2, value3),
(value1, value2, value3),
(value1, value2, value3); How can you see all rows in a table?
SELECT * FROM table_name; How can you show specific columns of a table?
This would show only column1 and column2 from the table.
SELECT column1, column2 FROM table_name; How do you order data when querying a table?
This would sort the table by column1 in descending order (DESC). To order in ascending order, use ASC.
SELECT column1, column2
FROM table_name
ORDER BY column1 DESC; How can you find unqiue values in a table?
Use the DISTINCT keyword as part of a query that eliminates duplicates and shows only unique values
SELECT DISTINCT column1
FROM table_name What is a great video?
🎉 All done!
No cards due for review right now.
Add some flashcards to get started!