PMDS508L - Python Programming
Module – 5 – Functions and Modules
Prepared by: Dr. Nandhini S, SAS
User-defined Functions
1. Functions allow us to organize code into reusable blocks.
2. They improve readability, reusability, and maintainability.
Defining a Function:
def function_name(arguements):
statements
return value
There are 4 types of arguments:
• Positional arguments → matched by position.
• Keyword arguments → matched by name.
• Default arguments → if not passed, default is used
• Variable-length arguments (*wrd, **wrds)
Positional and Keyword arguments:
# Program 1
def student(name, age):
print(f"Name: {name}, Age: {age}")
student("Alice", 21)
student(age=22, name="Bob")
# Program 2
def student_info(name, age, grade):
print(f"Name: {name}, Age: {age}, Grade: {grade}")
# Using keyword arguments
student_info(name="Alice", age=15, grade="10th")
student_info(grade="11th", age=16, name="Bob")
# change of order does not throw error
student_info("Charlie", grade="9th", age=14) # using both positional & Keyword
# can mix both but positional must be the first
student_info(grade="9th","Charlie", age=14) # Error
Points to ponder:
Positional arguments – considers the order.
Keyword arguments – dependent on parameter names.
Mixing - allowed, but positional must come first.
Default arguments:
# Program 1
def greet(name, msg="Good Morning"):
print(f"Hello {name}, {msg}")
greet("Alice")
greet("Bob", "How are you?")
# Program 2
def generate_report(name, marks, grade="Not Applicable", attendance=100,
scholarship=False, remarks=None):
print(f" ------ Student Report for {name} --------")
print(f"Marks: {marks}")
print(f"Grade: {grade}")
print(f"Attendance: {attendance}%")
print(f"Scholarship: {'Yes' if scholarship else 'No'}")
print(f"Remarks: {remarks if remarks else 'No remarks'}")
print("-" * 40)
# 1. Only mandatory arguments (defaults will be used)
generate_report("Alice", {"Math": 95, "Science": 89})
# 2. Overriding some defaults with keyword arguments
generate_report("Bob", {"Math": 70, "English": 75}, grade="B", attendance=92)
# 3. Passing everything
generate_report("Charlie", {"Math": 60, "History": 55}, grade="C", attendance=80,
scholarship=True, remarks="Needs improvement")
# 4. Mixing positional + keyword
generate_report("David", {"Science": 99}, scholarship=True)
Variable-length arguments:
# Program 1
def add_all(*args):
return sum(args)
print(add_all(1,2,3,4))
# Program 2
def add_all(*args): # passing arguments in a tuple
print("Arguments received:", args)
return sum(args)
print(add_all(1, 2)) # can pass any number of arguments as tuple
print(add_all(10, 20, 30, 40, 50))
print(add_all())
# Program 3
def demo(**wars): # ** passing arguments in a dictionary
print(wars)
demo(name="Alice", age=20)
# Program 4
def generate_report(name, marks, **margs):
print("=" * 40)
print(f"Report for {name}")
print("-" * 40)
# Display marks
total = sum([Link]())
avg = total / len(marks)
for subject, score in [Link](): # gives both keys and values
print(f"{subject:<10} : {score}")
print("-" * 40)
print(f"Total : {total}")
print(f"Average : {avg:.2f}")
# Display extra details passed using margs
if margs:
print("-" * 40)
print("Additional Details:")
for key, value in [Link]():
print(f"{[Link]():<12} : {value}") # <- left align the tex , > - right align, ^ - center align
print("=" * 40)
print()
# Calling the function
generate_report("Alice", {"Math": 95, "Science": 89}, grade="A", attendance=98)
generate_report("Bob", {"Math": 70, "English": 75}, grade="B", scholarship="Yes", remarks="Needs improvement")
generate_report("Charlie", {"History": 65, "Geography": 72}, address="123 Main St", phone="9876543210")
Namespaces and Scope Rules
A namespace is a system that maps names to objects to ensure all identifiers are unique within that context. Python
uses several types of namespaces:
✓ Built-in: Contains functions like len(), abs(), etc.
✓ Global: Defined at the top-level of a script/module.
✓ Local: Inside a function or method.
✓ Enclosing: For nested functions.
✓ A namespace is the environment that stores the mapping from the name → object.
✓ The order of resolution is known as the LEGB rule:
Local → Enclosed → Global → Built-in.
Example:
x = "This is an example for global"
def outer():
x = "This is an example for enclosed"
def inner():
x = "This is an example for local"
print(x) # 'local'
inner()
print(x) # 'enclosed'
outer()
print(x) # 'global'
Lambda Function
A lambda function is a small, anonymous function defined with the lambda keyword. It can take any number of
arguments but has only one expression. They are useful for short, throwaway functions.
Syntax: lambda arguments: expression
Example:1
square = lambda x: x * x
print("Square of 5:", square(5))
# def square(x): # equivalent to the above statement
# return x * x
Example:2
maximum = lambda a, b: a if a > b else b
print("Maximum of 10 and 7:", maximum(10, 7))
# def maximum(a, b):
# if a > b:
# return a
# else:
# return b
# print("Maximum of 1 and 7:", maximum(1, 7))
Example:3
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x*x, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers)) # returns the
values for which the function is TRUE
print(squared) # [1, 4, 9, 16, 25]
print(evens) # [2, 4]
Recursive Functions
✓ A recursive function is a function that calls itself. Each recursive call reduces the problem into smaller
subproblems. A base case is essential to stop recursion, otherwise infinite recursion occurs.
Example:1
def factorial(n):
if n == 0:
return 1 # base case
else:
return n * factorial(n-1)
print(factorial(5)) # 120
Example:2
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
print("Fibonacci of 6:", fibonacci(6))
Example:3
def reverse(s):
if s == "":
return s # base case: empty string
return reverse(s[1:]) + s[0]
reverse('hello')
Generator Functions
✓ Generator functions allow iteration over data without storing it all in memory.
✓ They use the 'yield' keyword to return one value at a time, pausing the function’s state until the next call.
yield vs return
• return → exits the function completely and gives a value.
• yield → pauses the function, gives a value, and remembers where it left off.
• This makes the function a generator instead of a normal function.
Example:1
def count_up_to(n):
count = 1
while count <= n:
yield count # pauses the function and send n to for loop
print(count)
count += 1
for number in count_up_to(5):
print(f"number is : {number}")
Example:2
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
print(list(fibonacci(10)))
==================== END ====================