1. Function fundamentals
- Arguments: positional, keyword,
*args,**kwargs, defaults, andonly positional/keyword-only(/and*markers). - Mutable default parametre trap: evaluated once at definition — shared across all calls.
def add(x, items=[]):
items.append(x)
return items
print(add(1)) # [1]
print(add(2)) # [1, 2] -- same list!
Fix: def add(x, items=None): items = [] if items is None else items.
- Call-by-assignment: mutations of mutable args affect the caller; rebinding the name locally does not.
returnwithout a value returnsNone.
2. LEGB scoping
- LEGB: Local → Enclosing → Global → Built-in. Name lookup climbs outward.
global x/nonlocal y— declare binding at those scopes.- Assignment anywhere in a function makes the name local — reading a global before assignment raises
UnboundLocalError.
x = 10
def f():
print(x) # UnboundLocalError!
x = 5
- Closures: inner function captures enclosing scope variables by reference (cell), still alive after outer returns.
3. Decorators
- A decorator is a callable taking a function and returning a (usually wrapped) function.
@decorator=func = decorator(func).- Preserve metadata with
functools.wraps. - Stacking: applied bottom-up, wraps innermost-out.
import functools, time
def timing(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
t = time.perf_counter()
r = fn(*args, **kwargs)
print(f"{fn.__name__}: {time.perf_counter()-t:.3f}s")
return r
return wrap
@timing
def work(): ...
- Decorators with args need one more nesting level:
@limit(3)→ returns the decorator. - Class-based decorators implement
__call__.
4. Generators & yield
- A function with
yieldis a generator function; calling it returns a generator object and runs nothing until iterated. - Lazy: values produced on demand — memory-efficient for large streams.
next(gen)fires until nextyield;StopIterationwhen exhausted.yield fromdelegates to a sub-generator.- A generator with only
return(no yield) is a plain function, not a generator.
def gen():
print("start")
yield 1
yield 2
g = gen() # nothing printed yet
print(next(g)) # "start" then 1
5. Lambda
- One-expression anonymous function:
lambda x: x * 2. - Useful for small
sorted(...key=),map/filter. - Gotcha: a
lambdacapturing loop variable binds by reference → classic[lambda: i for i in range(3)]returns the finalifor all (fix with default arglambda i=i: i).
6. Interview checkpoint
- Mutable default / late binding (closures, lambda default).
*args/**kwargs— closure of external libs.- Decorator ordering +
functools.wraps. - Generator vs list — when one-shot lazy beats eager (huge data, infinite streams).
return→Nonefallback.- Recursion depth limit (
sys.setrecursionlimit) — derived depth depends on each call frame.
Premium Content
Unlock Part 2: Functions, Scope & Decorators and all premium lessons with a subscription.
All premium lessons
Ad-free experience
Priority support
From ₹199.99/year — See plans