Functions
Prerequisites: Variables, data types, operators, loops, conditionals, strings, lists
Part A: Basics
1. What is a Function?
A function is a named block of reusable code that performs a specific task.
Why use functions?
- Reusability, write once, use many times
- Modularity, break large programs into smaller, manageable pieces
- Readability, makes code easier to understand
- Debugging, easier to find and fix errors
2. Types of Functions
| Type | Description | Examples |
|---|---|---|
| Built-in | Provided by Python | len, print, input, type |
| Module functions | From imported modules | math.sqrt, random.randint |
| User-defined | Written by the programmer | Functions you create with def |
3. Common Built-in Functions
# Type conversion
print(int("42")) # 42
print(float("3.14")) # 3.14
print(str(100)) # '100'
# Input/Output
name = input("Name: ") # reads string from user
print("Hello", name) # prints to screen
# Sequence functions
print(len("Hello")) # 5
print(len([1, 2, 3])) # 3
print(type(42)) # <class 'int'>
print(range(5)) # range(0, 5)
print(list(range(5))) # [0, 1, 2, 3, 4]
# Math
print(abs(-5)) # 5
print(round(3.7)) # 4
print(round(3.14159, 2)) # 3.14
print(max(3, 7, 1)) # 7
print(min(3, 7, 1)) # 1
print(sum([1, 2, 3])) # 6
print(pow(2, 3)) # 8
4. User-Defined Functions
4.1 Syntax
def function_name(parameters):
"""docstring (optional)"""
# body of function
# statements
return value # optional
4.2 Function with No Parameters, No Return
def greet:
print("Hello, World!")
# Calling the function
greet
greet
Output:
Hello, World!
Hello, World!
4.3 Function with Parameters
def greet(name):
print(f"Hello, {name}!")
greet("Rahul")
greet("Priya")
Output:
Hello, Rahul!
Hello, Priya!
4.4 Function with Return Value
def add(a, b):
return a + b
result = add(3, 5)
print("Sum:", result)
print("Sum:", add(10, 20))
Output:
Sum: 8
Sum: 30
4.5 Parameters vs Arguments
def greet(name): # 'name' is a PARAMETER (in definition)
print(f"Hello, {name}!")
greet("Rahul") # "Rahul" is an ARGUMENT (in call)
- Parameter: Variable in function definition (placeholder)
- Argument: Actual value passed when calling the function
5. The return Statement
5.1 Returning a Single Value
def square(n):
return n * n
result = square(5)
print(result)
Output:
25
5.2 Returning Multiple Values
def min_max(L):
return min(L), max(L) # returns a tuple
lo, hi = min_max([3, 1, 4, 1, 5])
print("Min:", lo)
print("Max:", hi)
Output:
Min: 1
Max: 5
5.3 None Return Type
When a function has no return statement (or just return with no value), it returns None.
def greet(name):
print(f"Hello, {name}!")
# no return statement
result = greet("Rahul")
print(result)
print(type(result))
Output:
Hello, Rahul!
None
<class 'NoneType'>
5.4 return Stops Function Execution
def check_positive(n):
if n > 0:
return "Positive"
return "Not positive"
print("This will never execute") # unreachable code
print(check_positive(5))
print(check_positive(-3))
Output:
Positive
Not positive
6. Calling a Function
def add(a, b):
return a + b
# Different ways to use the return value
result = add(3, 5) # store in variable
print(add(3, 5)) # print directly
total = add(3, 5) + add(2, 4) # use in expression
print(total)
# Function call as argument to another function
print(add(add(1, 2), add(3, 4))) # add(3, 7) = 10
Output:
8
14
10
Part B: Advanced
7. Types of Arguments
7.1 Positional Arguments
Arguments matched by position (order matters).
def introduce(name, age, city):
print(f"{name}, age {age}, from {city}")
introduce("Rahul", 17, "Delhi")
introduce("Delhi", "Rahul", 17) # wrong order, wrong output!
Output:
Rahul, age 17, from Delhi
Delhi, age Rahul, from 17
7.2 Default Arguments
Parameters with default values that are used when no argument is provided.
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Rahul") # uses default greeting
greet("Priya", "Good morning") # overrides default
Output:
Hello, Rahul!
Good morning, Priya!
Important rule: Default arguments must come after non-default arguments.
# CORRECT
def func(a, b, c=10):
pass
# WRONG -- SyntaxError
# def func(a, b=5, c):
# pass
7.3 Keyword Arguments
Arguments passed by name (order does not matter).
def introduce(name, age, city):
print(f"{name}, age {age}, from {city}")
# Using keyword arguments (order doesn't matter)
introduce(city="Delhi", name="Rahul", age=17)
introduce("Rahul", city="Delhi", age=17) # mix positional + keyword
Output:
Rahul, age 17, from Delhi
Rahul, age 17, from Delhi
Important rule: Keyword arguments must come after positional arguments.
# CORRECT
introduce("Rahul", age=17, city="Delhi")
# WRONG -- SyntaxError
# introduce(name="Rahul", 17, "Delhi")
7.4 Variable-Length Arguments (*args)
Accept any number of arguments.
def total(*args):
print("Arguments:", args)
print("Sum:", sum(args))
total(1, 2, 3)
total(10, 20, 30, 40, 50)
Output:
Arguments: (1, 2, 3)
Sum: 6
Arguments: (10, 20, 30, 40, 50)
Sum: 150
Note: *args collects extra positional arguments into a tuple. Know the concept; detailed *args / **kwargs questions are uncommon at the introductory level.
7.5 Summary of Argument Types
def demo(a, b, c=10, *args):
print(f"a={a}, b={b}, c={c}, args={args}")
demo(1, 2) # a=1, b=2, c=10, args=
demo(1, 2, 3) # a=1, b=2, c=3, args=
demo(1, 2, 3, 4, 5) # a=1, b=2, c=3, args=(4, 5)
Output:
a=1, b=2, c=10, args=
a=1, b=2, c=3, args=
a=1, b=2, c=3, args=(4, 5)
8. Scope of Variables
8.1 Local Scope (inside function)
Variables created inside a function are local, they exist only within that function.
def my_func:
x = 10 # local variable
print("Inside:", x)
my_func
# print(x) # NameError: name 'x' is not defined
Output:
Inside: 10
8.2 Global Scope (outside function)
Variables created outside any function are global, accessible everywhere.
x = 10 # global variable
def my_func:
print("Inside:", x) # can READ global variable
my_func
print("Outside:", x)
Output:
Inside: 10
Outside: 10
8.3 Local Variable Shadows Global Variable
x = 10 # global
def my_func:
x = 20 # local (shadows global)
print("Inside:", x)
my_func
print("Outside:", x) # global unchanged
Output:
Inside: 20
Outside: 10
8.4 The global Keyword
To modify a global variable inside a function, use the global keyword.
x = 10
def my_func:
global x # declare x as global
x = 20 # now modifies the global x
print("Inside:", x)
my_func
print("Outside:", x) # global is now 20
Output:
Inside: 20
Outside: 20
8.5 Scope Rules Summary
x = "global"
def outer:
x = "outer local"
def inner:
x = "inner local"
print("Inner:", x)
inner
print("Outer:", x)
outer
print("Global:", x)
Output:
Inner: inner local
Outer: outer local
Global: global
Rule: Python looks for a variable in this order:
- Local scope (current function)
- Enclosing scope (outer function, if nested)
- Global scope (module level)
- Built-in scope (Python's built-in names)
This is called the LEGB rule.
9. Flow of Execution
Python executes code line by line, but skips function bodies until the function is called.
def greet: # Line 1: function defined (body skipped)
print("Hello!") # Line 2: (skipped for now)
print("Start") # Line 4: prints "Start"
greet # Line 5: jumps to line 2, prints "Hello!"
print("End") # Line 6: prints "End"
Output:
Start
Hello!
End
Trace of execution:
- Line 1:
greetfunction is defined (body not executed) - Line 4:
"Start"is printed - Line 5:
greetis called, execution jumps into the function - Line 2:
"Hello!"is printed - Function ends, execution returns to after line 5
- Line 6:
"End"is printed
10. Functions Calling Other Functions
def is_even(n):
return n % 2 == 0
def count_even(L):
count = 0
for item in L:
if is_even(item): # calling is_even from count_even
count += 1
return count
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
print("Even count:", count_even(numbers))
Output:
Even count: 4
11. Recursion
A function that calls itself is called a recursive function.
Every recursive function must have:
- Base case, condition to stop recursion
- Recursive case, function calls itself with a simpler input
def factorial(n):
if n == 0 or n == 1: # base case
return 1
else:
return n * factorial(n - 1) # recursive case
print(factorial(5))
Output:
120
How it works (trace for factorial(5)):
factorial(5)
= 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
12. Practice Programs
Program 1: Function to check even/odd
def check_even_odd(n):
if n % 2 == 0:
return "Even"
else:
return "Odd"
num = int(input("Enter a number: "))
print(f"{num} is {check_even_odd(num)}")
Sample Run:
Enter a number: 7
7 is Odd
Program 2: Function to find factorial (iterative)
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
num = int(input("Enter a number: "))
print(f"Factorial of {num} = {factorial(num)}")
Sample Run:
Enter a number: 5
Factorial of 5 = 120
Program 3: Function to find factorial (recursive)
def factorial(n):
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
num = int(input("Enter a number: "))
print(f"Factorial of {num} = {factorial(num)}")
Sample Run:
Enter a number: 6
Factorial of 6 = 720
Program 4: Function to check prime
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
num = int(input("Enter a number: "))
if is_prime(num):
print(f"{num} is a prime number")
else:
print(f"{num} is not a prime number")
Sample Run:
Enter a number: 17
17 is a prime number
Program 5: Function to reverse a string
def reverse_string(s):
return s[::-1]
# Alternative: recursive version
def reverse_recursive(s):
if len(s) <= 1:
return s
return reverse_recursive(s[1:]) + s[0]
text = input("Enter a string: ")
print("Reversed:", reverse_string(text))
print("Reversed (recursive):", reverse_recursive(text))
Sample Run:
Enter a string: Python
Reversed: nohtyP
Reversed (recursive): nohtyP
Program 6: Function with default arguments
def power(base, exponent=2):
"""Return base raised to exponent. Default exponent is 2 (square)."""
return base ** exponent
print(power(5)) # 5^2 = 25 (uses default)
print(power(5, 3)) # 5^3 = 125
print(power(2, 10)) # 2^10 = 1024
Output:
25
125
1024
Program 7: Function returning multiple values
def analyze_list(L):
"""Return min, max, sum, and mean of a list."""
return min(L), max(L), sum(L), sum(L)/len(L)
numbers = [10, 20, 30, 40, 50]
minimum, maximum, total, average = analyze_list(numbers)
print(f"Minimum: {minimum}")
print(f"Maximum: {maximum}")
print(f"Sum: {total}")
print(f"Average: {average}")
Output:
Minimum: 10
Maximum: 50
Sum: 150
Average: 30.0
Program 8: Fibonacci using recursion
def fibonacci(n):
"""Return the nth Fibonacci number (0-indexed)."""
if n == 0:
return 0
elif n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
# Print first 10 Fibonacci numbers
for i in range(10):
print(fibonacci(i), end=" ")
print
Output:
0 1 1 2 3 5 8 13 21 34
How it works (trace for fibonacci(4)):
fibonacci(4)
= fibonacci(3) + fibonacci(2)
= (fibonacci(2) + fibonacci(1)) + (fibonacci(1) + fibonacci(0))
= ((fibonacci(1) + fibonacci(0)) + 1) + (1 + 0)
= ((1 + 0) + 1) + (1 + 0)
= (1 + 1) + 1
= 3
Program 9: Tower of Hanoi (recursion)
def hanoi(n, source, auxiliary, destination):
"""Move n disks from source to destination using auxiliary."""
if n == 1:
print(f"Move disk 1 from {source} to {destination}")
return
hanoi(n - 1, source, destination, auxiliary)
print(f"Move disk {n} from {source} to {destination}")
hanoi(n - 1, auxiliary, source, destination)
n = int(input("Enter number of disks: "))
print(f"\nSolving Tower of Hanoi for {n} disk(s):")
hanoi(n, 'A', 'B', 'C')
Sample Run:
Enter number of disks: 3
Solving Tower of Hanoi for 3 disk(s):
Move disk 1 from A to C
Move disk 2 from A to B
Move disk 1 from C to B
Move disk 3 from A to C
Move disk 1 from B to A
Move disk 2 from B to C
Move disk 1 from A to C
Total moves needed = 2^n, 1 (for 3 disks: 7 moves)
Program 10: Function to count vowels in string
def count_vowels(s):
count = 0
for ch in s.lower:
if ch in "aeiou":
count += 1
return count
text = input("Enter a string: ")
print(f"Number of vowels: {count_vowels(text)}")
Sample Run:
Enter a string: Hello World
Number of vowels: 3
Shorter version:
def count_vowels(s):
return sum(1 for ch in s.lower if ch in "aeiou")
13. Common Mistakes
| Mistake | Why it is wrong | Correct way |
|---|---|---|
def func(a, b=5, c): |
Default after non-default | def func(a, c, b=5): |
Using mutable default: def f(L=[]): |
Default list is shared across calls | def f(L=None): L = L or [] |
Forgetting return |
Function returns None | Add return value |
print(greet) when greet just prints |
Prints the message AND "None" | Either return or print, not both |
Modifying global without global keyword |
Creates local variable instead | Use global x before modifying |
| Infinite recursion (no base case) | RecursionError: maximum depth exceeded | Always have a base case |
func(name="A", "B") |
Keyword before positional | func("B", name="A") or fix order |
$1$2 Quick Tips
-
Types of arguments (positional, default, keyword) is a very frequent question (2-3 marks). Be ready to identify each type in a given function call.
-
Scope of variables (local vs global,
globalkeyword) is asked almost every year. Practice tracing through code to predict output. -
Return value questions: "What does this function return?", trace the code. If there is no
return, the answer isNone. -
Difference between parameters and arguments, parameters are in the definition, arguments are in the call.
-
Recursion programs (factorial, Fibonacci) are common 4-5 mark questions. Always show the base case clearly.
-
Flow of execution: Be able to trace which lines execute and in what order when functions call each other.
-
Default arguments rule: Default parameters must come after non-default parameters in the definition.
-
Mutable vs immutable arguments: When you pass a list to a function and modify it, the original list changes (because lists are mutable). When you pass a number or string, the original is unaffected.
def modify(L, n):
L.append(4) # modifies original list
n = n + 10 # does NOT modify original number
my_list = [1, 2, 3]
my_num = 5
modify(my_list, my_num)
print(my_list) # [1, 2, 3, 4] -- changed!
print(my_num) # 5 -- unchanged
Prefer watching over reading?
Subscribe for free.