Lesson 11 of 2013 min read

Functions

Share:WhatsAppLinkedIn

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:

  1. Local scope (current function)
  2. Enclosing scope (outer function, if nested)
  3. Global scope (module level)
  4. 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:

  1. Line 1: greet function is defined (body not executed)
  2. Line 4: "Start" is printed
  3. Line 5: greet is called, execution jumps into the function
  4. Line 2: "Hello!" is printed
  5. Function ends, execution returns to after line 5
  6. 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:

  1. Base case, condition to stop recursion
  2. 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

  1. 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.

  2. Scope of variables (local vs global, global keyword) is asked almost every year. Practice tracing through code to predict output.

  3. Return value questions: "What does this function return?", trace the code. If there is no return, the answer is None.

  4. Difference between parameters and arguments, parameters are in the definition, arguments are in the call.

  5. Recursion programs (factorial, Fibonacci) are common 4-5 mark questions. Always show the base case clearly.

  6. Flow of execution: Be able to trace which lines execute and in what order when functions call each other.

  7. Default arguments rule: Default parameters must come after non-default parameters in the definition.

  8. 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

Test Your Knowledge

Take a quick quiz on this lesson

Start Quiz →

Prefer watching over reading?

Subscribe for free.

Subscribe on YouTube