Functions¶
A function is a reusable block of code with a name. Define once, use many times.
Defining a function¶
def defines a function. The indented block is its body. The function doesn't run until you call it with ().
Functions with parameters¶
Parameters are inputs to the function.
Multiple parameters¶
return sends a value back to the caller. Without return, a function returns None.
Default parameter values¶
Make a parameter optional by giving it a default:
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", greeting="Yo") # Yo, Charlie!
Keyword arguments¶
Pass arguments by name — order doesn't matter:
def create_user(name, age, city):
print(f"{name}, {age} years old, lives in {city}")
# Positional — order matters
create_user("Alice", 25, "Mumbai")
# Keyword — clearer, order doesn't matter
create_user(age=25, city="Mumbai", name="Alice")
Variable-length arguments — *args and **kwargs¶
When you don't know how many arguments will be passed:
# *args collects extra positional args as a tuple
def add_all(*numbers):
return sum(numbers)
print(add_all(1, 2, 3)) # 6
print(add_all(1, 2, 3, 4, 5, 10)) # 25
# **kwargs collects extra keyword args as a dict
def print_info(**details):
for key, value in details.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="Mumbai")
Returning multiple values¶
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(f"Min: {low}, Max: {high}")
You're really returning a tuple that gets unpacked.
Docstrings — document your function¶
A string right after def is the function's docstring. help() shows it.
def area_of_circle(radius):
"""
Return the area of a circle.
Args:
radius: the radius of the circle in any unit
Returns:
the area in unit²
"""
return 3.14159 * radius * radius
print(area_of_circle(5))
help(area_of_circle)
Scope — local vs global variables¶
Variables inside a function are local — they only exist inside that function.
x = 10 # global
def show():
x = 99 # local — different from the global x
print("inside:", x)
show()
print("outside:", x)
To modify a global from inside a function, use global:
count = 0
def increment():
global count
count += 1
increment()
increment()
increment()
print(count) # 3
Avoid global when possible. Better: return the value and let the caller assign it.
Recursion — a function that calls itself¶
A classic example — computing factorial: n! = n × (n-1)!
def factorial(n):
if n <= 1:
return 1 # base case
return n * factorial(n - 1) # recursive case
print(factorial(5)) # 5*4*3*2*1 = 120
print(factorial(7)) # 5040
Every recursive function needs: 1. Base case — stops the recursion. 2. Recursive case — calls itself with a smaller problem.
Without a base case → infinite recursion → RecursionError.
lambda — anonymous one-liner functions¶
lambda parameters: expression — a function without a name, useful as a one-time argument.
# Same as: def square(x): return x * x
square = lambda x: x * x
print(square(5)) # 25
# Most useful when passing to another function
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_desc = sorted(numbers, key=lambda x: -x)
print(sorted_desc)
map, filter, reduce¶
Three functional-programming building blocks.
map — apply a function to every item:
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x * x, numbers))
print(squares) # [1, 4, 9, 16, 25]
# Same with a list comprehension (more Pythonic)
squares2 = [x * x for x in numbers]
print(squares2)
filter — keep only items where the function returns True:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# Same with comprehension
evens2 = [x for x in numbers if x % 2 == 0]
print(evens2)
reduce — combine all items into one value (need to import):
from functools import reduce
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda acc, x: acc + x, numbers)
print(total) # 1+2+3+4+5 = 15
product = reduce(lambda acc, x: acc * x, numbers)
print(product) # 1*2*3*4*5 = 120
Practice¶
What does this print?
Expected: Hi, Alice!
Return the sum of all args (not just the first one)
Expected: 10
Quiz — Quick check¶
What you remember
Q1. What does a function return if you don't write a return statement?
-
0 -
False -
None - An error
Why: Every Python function implicitly returns
Noneif noreturnis reached.
Q2. What does *args collect arguments into?
- A list
- A tuple
- A dict
- A set
Why:
*argspacks extra positional arguments into a tuple.**kwargspacks extra keyword arguments into a dict.
Q3. A recursive function must have a…
-
returnofNone -
globaldeclaration - base case
-
lambdaform
Why: Without a base case (a stopping condition), the function keeps calling itself until Python raises
RecursionError.
Common doubts¶
What's the difference between a parameter and an argument?
A parameter is the name in the function definition (def add(a, b) → a and b). An argument is the value you pass when calling (add(5, 3) → 5 and 3). People often use the terms interchangeably; in interviews you'll hear "parameters" for definitions and "arguments" for calls.
When should I use lambda?
Use lambda when you need a tiny throwaway function inline — typically as the key= argument to sorted, max, min, or with map/filter. For anything multi-line or reused, write a real def — it's more readable and easier to debug.
Are default arguments evaluated once or every call?
Once, when the function is defined. This causes a classic bug:
Callingadd_to(1) twice gives [1, 1], not [1] each time. Use target=None and create [] inside the function instead.
What's next¶
→ Strings