7. Functions and Modules
Higher Secondary · Computer Science · Science Stream

Functions and Modules

Breaking large programs into smaller, manageable subprograms — how Python organises code as built-in, imported, and user-defined functions, how arguments and return values pass information around, and how modules, packages, and libraries stack into a hierarchy.

7.2 · Live Tool

Built-in Function Lab

Pick a built-in function, type in an argument the way you would inside Python's IDLE Shell — numbers as-is, text in quotes, comma-separated values for multi-argument functions — and watch it evaluate.

Try a Built-in Function

● live
Choose a function, type an argument, and press Run.
Numbers: 23.5 · Strings: 'hello' · Multiple values: 9, 23, 17, 3 · round with digits: 123.785, 2
7.1

Functions in Python

As problems grow larger, program code becomes longer and harder to read, understand, and maintain. Breaking a large program into smaller, manageable parts — called subprograms — solves this. Related subtasks are grouped into a single unit called a module; this process of breaking a problem into smaller sub-problems is called modularisation. Each subtask within a module is placed in a function.

Arguments and Return Values

A function receives zero or more input values — called arguments (sometimes called parameters) — and returns zero or more output values, called return values.

Why Functions Help

A function written once — for example, to find total marks — can be reused several times across a program. This enhances code reusability and reduces both duplication and overall code size.

Example — School Management System Admission Academics Accounts & Fees Staff Management └─ each major subtask → a MODULE └─ each subtask within a module → a FUNCTION e.g. functions for total marks, averages, progress reports

Python classifies functions into three kinds: built-in functions, imported functions (from modules), and user-defined functions.

7.2

Built-in Functions

Python's standard library ships with a large collection of predefined functions, ready to use without being defined by the programmer. Here are the ones this chapter covers.

a. Type Conversion Functions

FunctionInput argument(s)Return type & valueRemarks
int()Numeric expression as number or a string containing whole numbersInteger — integer part of the valueIf a string argument contains non-numeric characters, it returns an error
float()Numeric expression as numbers or a stringFloat — value of the expressionNon-numeric characters in a string argument cause an error
str()Any data typeString — the object converted to string typeWorks with any input
bool()Numeric expression, number, string, list, tuple, or dictionaryBoolean — True/FalseReturns False for False, None, 0, an empty string, or any empty collection; True otherwise

b. eval() Function

Takes an expression as a string and returns the resulting value. It works only with Python expressions — not statements like if, for, or while.

c. max() and min() Functions

max() returns the largest value in a group; min() returns the smallest. When comparing strings, dictionary order applies, and the comparison is case sensitive — since upper-case characters have lower Unicode values, all upper-case letters are treated as "earlier" than all lower-case letters.

abs(x)

Returns the positive equivalent of a numeric argument. Errors if the input doesn't evaluate to a number.

type()

Accepts a variable, literal, or expression and returns its data type, e.g. <class 'int'>.

len()

Finds the number of characters in a string, or the number of elements in a list, tuple, set, or dictionary.

id()

Returns a unique ID representing an object's memory reference — useful to check whether two variables point to the same object.

round(n, d) Function

Rounds n to d decimal places. If d is omitted, it rounds to the nearest integer. If d is negative, it rounds to the nearest 10|d| — e.g. round(2673.23, -2) gives 2700.

Banker's Rounding round() treats a fractional part of exactly .5 specially: it rounds to the nearest EVEN number. round(126.5) → 126 round(127.5) → 128 This "round half to even" method balances rounding up and down, reducing the cumulative bias that always-round-up would introduce over many financial, scientific, or statistical calculations.
7.3

Imported Functions

Related predefined functions are grouped into modules according to purpose — the math module holds mathematical functions, statistics holds statistical ones. The keyword import brings a module's functions into a program.

a. Importing Entire Modules

import math import math, statistics import statistics as stat

Functions are then accessed with the dot operator: math.sqrt(), or via an alias: stat.mean().

b. Importing Selected Methods

from math import sqrt from math import sqrt, pow, cos from math import *

Importing only what's needed keeps the translated program code smaller than pulling in an entire module.

7.3.1 math Module

FunctionDescriptionExample
sqrt(x)Square root of x; runtime error if x is negativemath.sqrt(23) → 4.795…
pow(x, y)xy; result is always a floatmath.pow(4, 3) → 64.0
ceil(x)Closest integer ≥ xmath.ceil(23.34) → 24
floor(x)Closest integer ≤ xmath.floor(-23.78) → -24
fabs(x)Absolute value, always returned as a floatmath.fabs(-43) → 43.0
sin(x) / cos(x)Sine / cosine of x, given in radiansmath.sin(1.5708) → ≈1.0

7.3.2 statistics Module

Part of Python's standard library — no external installation needed. All functions work on a group of values: a list, tuple, or set.

FunctionDescription
mean(data)Arithmetic mean (average) of the numeric data
median(data)Middle value of the numeric data
mode(data)Most frequently occurring value (central tendency)
variance(data)How far the data is spread out from the mean; large variance = more spread
stdev(data)Standard deviation — the square root of variance, in the same units as the data
Using an alias import statistics as stat stat.mean([1, 4, 6, 7, 9, 11]) → 6.333333333333333 stat.median([1, 4, 6, 7, 9, 11]) → 6.5 stat.mode([1, 4, 6, 7, 4, 11]) → 4 stat.stdev([1, 4, 6, 7, 4, 11]) → 3.391164991562634
7.4

User-defined Functions

A user-defined function is written to perform a specific task according to the programmer's requirements — defined once, then called wherever it's needed.

7.4.1 Defining a Function

def function_name(argument_list): function_statement1 function_statement2

def starts the definition. The function body is written in proper indentation. A return keyword is used only in functions that have at least one value to send back to the caller — a function with no return statement (or a bare return) defaults to returning None.

def welcome(): print("Welcome to Python functions")

7.4.2 Calling a Function & Flow of Execution

A function executes only when it is called — welcome(). Python processes all function definitions first when reading the program but skips over them; execution begins at the first statement outside any function. When a call is reached, control transfers into the function, runs until the last statement or a return, then shifts back to right after the call.

def welcome(): print("Welcome to Python functions") def line(): print("========================") line() welcome() line() Output: ======================== Welcome to Python functions ========================

7.4.3 Passing Values and Returning Values

Simple interest, single return value def interest(P, N, R): si = P * N * R / 100 return si principal = float(input("Enter Principal Amount: ")) rate = float(input("Enter Rate of Interest: ")) years = float(input("Enter No. of Years: ")) simple = interest(principal, years, rate) print("Simple Interest is: ", simple) Enter Principal Amount: 10000 Enter Rate of Interest: 12 Enter No. of Years: 2 Simple Interest is: 2400.0

Actual Parameters

The values/variables used in the function call — here, principal, years, rate.

Formal Parameters

The variables used in the function definition — here, P, N, R.

Returning multiple values (packed into a tuple) def interest(P, N, R): si = P*N*R/100 ci = (P*(1+R/100)**N) - P return si, ci simple, compound = interest(10000, 2, 12) print("Simple Interest is: ", simple) # 2400.0 print("Compound Interest is: ", compound) # 2544.000000000002

When values separated by commas are returned, Python automatically packs them into a tuple; at the call site, the tuple is unpacked into the receiving variables. A function's argument type matters too — passing a float like 5.5 to a factorial() function built around range() causes a runtime error, since range() only accepts integers. Validating argument types avoids such errors.

7.5

Types of Arguments

Python supports four ways of passing arguments into a function call.

7.5.1 Positional Arguments

Passed in the same order as the formal argument list. interest(1000, 2, 12) maps 1000→P, 2→N, 12→R strictly by position.

7.5.2 Default Arguments

Given a default value in the definition using = — used only when the caller omits that argument. Default arguments must be the right-most ones in the list.

7.5.3 Keyword Arguments

Passed by explicitly naming the argument: interest(R=12, P=1000, N=2). Order no longer matters. Any positional arguments present must come first, left of the keyword ones.

7.5.4 Variable Length Arguments

Prefixing a parameter with *, e.g. *numbers, lets a function accept any number of values — Python packs them into a tuple internally.

Default argument — R defaults to 10 def interest(P, N, R=10): si = P*N*R/100 print("Simple Interest is: ", si) interest(1000, 2) → Simple Interest is: 200.0 (R takes default 10) interest(1000, 2, 12) → Simple Interest is: 240.0 (R overridden to 12)
Variable length arguments def average(*numbers): sum = 0 count = 0 for n in numbers: sum += n count += 1 return sum/count print("Average is: ", average(10, 20, 30)) → Average is: 20.0 print("Average is: ", average(5, 15)) → Average is: 10.0

The print() function's sep and end parameters — print(value, sep='', end='\n') — are a familiar everyday example of keyword arguments.

7.6

Scope Rules

An assignment to a variable creates a reference to an object — but that reference remains valid only within certain parts of the program. Python's scope rules define exactly where.

7.6.1 Local Scope

A variable assigned inside a function is accessible only inside that function. Accessing it from outside raises NameError: name 'x' is not defined.

7.6.2 Global Scope

A variable defined outside all functions is globally valid — accessible from any part of the program, including inside functions (with one exception, below).

7.6.3 Overriding

Redefining a global variable's name inside a function creates a new local variable that shadows it — the global value is left untouched outside the function.

Overriding — the local x shadows the global x x = 10 # Global definition def test(): x = 20 # Local definition — overrides only inside test() print("Inside Function: ", x) print("Before calling Function: ", x) → 10 test() → Inside Function: 20 print("After calling Function: ", x) → 10 (global x unchanged)
Bypassing overriding with the global keyword x = 10 def test(): global x # refers to the x defined in global scope x = x + 5 print("Inside Function: ", x) test() → Inside Function: 15 print("Outside Function: ", x) → 15 (global x WAS modified)

Declaring global x inside a function tells Python to use and modify the global x instead of creating a new local one with the same name.

7.7 · Live Tool

Recursive Functions

Recursion is a technique where a function solves a problem by calling itself on a simpler version of the same problem, until a base case is reached that can be answered directly. Every recursive function has a base case (stops the recursion) and a recursive part (calls itself with modified arguments, moving closer to the base case).

Trace a Recursive Call

● live
Pick a function, enter a value, and press Trace.
Shows the calls going down to the base case, then the results unwinding back up — like Figure 7.35 in the chapter. Keep n small (0–12) to stay readable.
factorial() — base case and recursive part def factorial(n): if n < 0: return None # Error case if (n == 1) or (n == 0): return 1 # Base case else: return n * factorial(n - 1) # Recursive part n! = n * (n-1)! and recursion terminates at 1! = 1
sumofdigits() — divides the problem into last digit + rest def sumofdigits(n): if n == 0: # Base case return 0 return (n % 10) + sumofdigits(n // 10)
fibonacci() — each term is the sum of the two before it def fibonacci(n): if n < 0: return None if (n == 1) or (n == 0): # Base cases: fib(0)=0, fib(1)=1 return n else: return fibonacci(n-1) + fibonacci(n-2) fibonacci(0..4) → 0 1 1 2 3

Recursion makes it easier to write and understand solutions for problems that naturally break into smaller subproblems, reducing lengthy code into shorter, cleaner functions. The trade-off: recursion uses more memory and can run slower than an equivalent loop.

7.8

Creating and Using User-defined Modules

Beyond built-in modules like math and statistics, programmers can create their own — a user-defined module is simply a .py file containing function, variable, or class definitions.

calculator.py — a user-defined module def add(a, b): return a + b def subtract(a, b): return a - b def multiply(a, b): return a * b def divide(a, b): return a / b

Importing the Whole Module

import calculator a = 20; b = 5 print("Addition: ", calculator.add(a, b))

Output: Addition: 25

Importing One Function

from calculator import divide a = 20; b = 5 print("Division: ", divide(a, b))

Output: Division: 4.0

Breaking a program into modules produces cleaner, more maintainable, and more reusable code.

7.9

Hierarchical Building Blocks of Python Programs

A Python program is organised as a clear hierarchy, growing from small units of code into larger collections of related tools — helping programmers manage large projects by reusing and organising code.

7.9.1 Function

A block of organised, reusable code performing a specific task — breaks a large program into smaller parts, easier to write, test, and maintain.

7.9.2 Module

A file containing Python code — functions, classes, or variables — logically organised and reusable across programs via import.

7.9.3 Package

A collection of related modules grouped in a directory. Must contain a special (possibly empty) file named __init__.py to be recognised as a package. NumPy, Pandas, Matplotlib are external packages.

7.9.4 Library

A collection of built-in and external modules and packages providing ready-to-use functionality for mathematics, data science, web development, and more.

Function → grouped into a Module → related Modules grouped into a Package → Packages and Modules together form a Library
Let Us Do

Quick Recap

Tap a question to reveal the answer.

What does int('hello') do, and why?

It raises a ValueError: invalid literal for int() with base 10: 'hello' — int() can only convert a string to an integer if that string contains numeric characters representing a whole number.

What does eval(34) return — an error, or a value? Why?

It raises a TypeError — eval() requires its argument to be a string (or bytes/code object) containing an expression, not a bare number.

Why does round(126.5) give 126 while round(127.5) gives 128?

Python's round() uses Banker's Rounding (round half to even) — a value exactly halfway between two integers rounds to whichever of the two is even, reducing cumulative bias over many calculations.

In max('Kozhikode', 'kozhikode', 'Calicut', 'calicut'), why does 'kozhikode' (lower-case k) win over 'Kozhikode'?

String comparison in Python is case sensitive, and all upper-case characters have lower Unicode values than all lower-case characters — so a lower-case-starting string is treated as "greater" in dictionary order than the same word starting with an upper-case letter.

What is the default return value of a function that has no return statement, or a bare return?

None — the keyword representing the absence of a value.

What's the difference between actual parameters and formal parameters?

Actual parameters are the values/variables used in the function call statement. Formal parameters are the variable names used in the function definition. Values flow from actual to formal parameters when the function is called.

Why must default arguments be the right-most arguments in a function definition?

Because arguments are matched to parameters by position unless named explicitly — if a default argument came before a required one, Python couldn't tell which values the caller intended to skip.

Spot the issue: a global variable total = 0 is reassigned inside a function as total = total + 5 without declaring global total first.

This raises an UnboundLocalError. The moment Python sees an assignment to total anywhere inside the function, it treats total as local for the whole function body — so total + 5 tries to read a local variable that hasn't been assigned yet. Adding global total fixes it.

What happens if a recursive function is written without a base case?

The function keeps calling itself indefinitely and never terminates, eventually causing the Python interpreter to flag an error (a recursion depth / stack error) instead of returning a result.

Why does factorial(5.5) fail in a factorial function built around range(1, n+1)?

Because range() only accepts integer arguments — passing a float raises a runtime error. This is why validating the type of arguments before processing is important.

What must a folder contain for Python to treat it as a package rather than just a directory of scripts?

A special file named __init__.py, which can be empty — its presence is what Python uses to recognise the directory as a package.