Functions, Closures, and Lambda
Define reusable helper functions, use default arguments, and apply lambda expressions to sorting and functional patterns in interview problems.
Functions, Closures, and Lambda is a free DSA Interview Prep lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DSA Interview Prep learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Defining Functions in Python
Functions are first-class in Python: you can pass them around, return them, and store them. Well-named helpers make interview code readable. Handle edge cases first.
def is_palindrome(s: str) -> bool:
'''Return True if s reads the same forwards and backwards.'''
s = s.lower()
return s == s[::-1]
print(is_palindrome('Racecar')) # True
print(is_palindrome('hello')) # False
# Functions as values
checks = [is_palindrome]
print(checks[0]('level')) # TrueDefault and Keyword Arguments
Defaults let callers skip arguments. But never use a mutable default like a list — all callers share it. The fix: default to None and create the list inside. See the code.
# WRONG: shared mutable default
def bad_append(val, lst=[]):
lst.append(val)
return lst
print(bad_append(1)) # [1]
print(bad_append(2)) # [1, 2] surprise!
# CORRECT: use None sentinel
def good_append(val, lst=None):
if lst is None:
lst = []
lst.append(val)
return lst
print(good_append(1)) # [1]
print(good_append(2)) # [2]*args and **kwargs
*args gathers extra positional arguments into a tuple; **kwargs gathers extra keyword ones into a dict. The * also unpacks a sequence when you call a function.
def total(*args):
return sum(args)
print(total(1, 2, 3)) # 6
print(total(1, 2, 3, 4)) # 10
# Unpack a list as positional args
point = [3, 7]
print(max(*point)) # 7
# **kwargs
def greet(**kwargs):
name = kwargs.get('name', 'World')
return f'Hello, {name}!'
print(greet(name='Alice')) # Hello, Alice!Lambda Expressions
A lambda is a tiny one-line function: lambda params: expression. Perfect as a quick key in sorted or max. For anything bigger, a named def reads more clearly.
# Lambda as sort key
pairs = [(1, 3), (2, 1), (3, 2)]
sorted_by_second = sorted(pairs, key=lambda p: p[1])
print(sorted_by_second) # [(2,1),(3,2),(1,3)]
# Lambda with map
double = list(map(lambda n: n * 2, [1, 2, 3]))
print(double) # [2, 4, 6]
# Named function is clearer for complex logic
def sort_key(p):
return (p[1], -p[0]) # secondary sort
print(sorted(pairs, key=sort_key))Nested Functions and Closures
A closure is a function that remembers variables from the function around it. Define a helper inside another function and it can use the outer variables — handy for DFS.
def make_counter(start=0):
count = [start] # list to allow mutation
def increment():
count[0] += 1
return count[0]
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
# Alternative: nonlocal keyword
def make_counter2():
count = 0
def increment():
nonlocal count
count += 1
return count
return incrementClosures in Recursive DFS
A common pattern: define a dfs helper inside the main function and let it collect results into an outer variable. The inner function is a closure over that scope.
def max_depth(root):
'''Closure pattern for tree DFS.'''
max_d = [0] # mutable container for closure
def dfs(node, depth):
if node is None:
return
max_d[0] = max(max_d[0], depth)
dfs(node.left, depth + 1)
dfs(node.right, depth + 1)
dfs(root, 1)
return max_d[0]
# This pattern avoids a class or global state.functools.lru_cache for Memoisation
lru_cache (or @cache in 3.9+) remembers a function's results by its arguments, turning naive Fibonacci from O(2^n) into O(n). Arguments must be hashable, so use tuples.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
print(fib(40)) # 102334155 fast!
print(fib.cache_info()) # hits, misses, sizeHigher-Order Functions
A higher-order function takes or returns a function. sorted, map, and filter all qualify. Writing your own lets you abstract repeated patterns. The code shows composition.
def apply_twice(f, x):
return f(f(x))
print(apply_twice(lambda n: n * 2, 3)) # 12 (3*2*2)
# Composing transformations
def compose(f, g):
return lambda x: f(g(x))
double = lambda n: n * 2
add_one = lambda n: n + 1
double_then_add = compose(add_one, double)
print(double_then_add(5)) # 11 ((5*2)+1)Recursion with Helper Functions
Many solutions pair an outer function that handles setup and edge cases with an inner helper that does the recursion. Clean public API, isolated logic. See the code.
def flatten(nested):
'''Flatten an arbitrarily nested list.'''
result = []
def _flatten(lst):
for item in lst:
if isinstance(item, list):
_flatten(item)
else:
result.append(item)
_flatten(nested)
return result
print(flatten([1, [2, [3, 4], 5], 6]))
# [1, 2, 3, 4, 5, 6]Partial Functions with functools.partial
functools.partial pre-fills some arguments and hands back a new callable. Useful when an API wants a no-argument function but you need one fixed parameter set.
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
cube = partial(power, exp=3)
print(square(5)) # 25
print(cube(3)) # 27
print(list(map(square, [1, 2, 3, 4]))) # [1, 4, 9, 16]Lambda for Custom Comparators
Python 3 dropped two-argument comparators, but cmp_to_key brings them back for tricky orderings — like arranging numbers so their concatenation is largest. See the code.
from functools import cmp_to_key
def largest_number(nums):
def compare(a, b):
if a + b > b + a: return -1
if a + b < b + a: return 1
return 0
strs = list(map(str, nums))
strs.sort(key=cmp_to_key(compare))
result = ''.join(strs)
return '0' if result[0] == '0' else result
print(largest_number([3, 30, 34, 5, 9])) # '9534330'Quick Check
Quick check — time to show what you learned about functions, closures, and lambdas. Take a breath and go. 🚀
Lesson Recap
Recap: never use mutable defaults (use None), closures let inner helpers reach outer variables for DFS, and functools tools like lru_cache power fast solutions.
Frequently asked questions
Is the “Functions, Closures, and Lambda” lesson free?
Yes — the full text of “Functions, Closures, and Lambda” is free to read here on the web, and the DSA Interview Prep course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DSA Interview Prep course, upgrade to CoddyKit PRO.
What will I learn in “Functions, Closures, and Lambda”?
Define reusable helper functions, use default arguments, and apply lambda expressions to sorting and functional patterns in interview problems. You practise DSA Interview Prep with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start DSA Interview Prep?
No prior experience is required. DSA Interview Prep on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Functions, Closures, and Lambda” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this DSA Interview Prep lesson?
Yes. Every DSA Interview Prep lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Lists, Tuples, and Slicing
- Dictionaries and Sets in Python
- Comprehensions and Built-ins
- Functions, Closures, and Lambda