Functions

Functions are named, reusable blocks of code that perform a specific task. They are one of the most important tools in programming because they let you write code once and use it many times.

Why This Chapter Matters

Well-written functions make programs easier to read, test, and change. Almost every Python program you will write depends on functions.

  • they reduce code repetition (DRY: Don't Repeat Yourself)
  • they give code clear names and structure
  • they let you test pieces of code in isolation
  • they make programs easier to change and maintain

Defining a Function

Use the def keyword, followed by the function name, parameters in parentheses, and a colon.

def greet(name):
    return f"Hello, {name}!"

message = greet("Asha")
print(message)   # Hello, Asha!

Parameters and Arguments

  • Parameters are the variable names in the function definition
  • Arguments are the actual values passed when calling the function
def add(a, b):     # a and b are parameters
    return a + b

result = add(3, 7)  # 3 and 7 are arguments
print(result)       # 10

The return Statement

return sends a value back to the caller and ends the function immediately.

def square(n):
    return n * n

print(square(5))   # 25

Without return, a function returns None:

def say_hello():
    print("Hello!")

result = say_hello()
print(result)   # None

A function can return multiple values as a tuple:

def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([3, 1, 9, 4])
print(low, high)   # 1 9

Default Parameters

Provide a default value so the argument becomes optional:

def power(base, exponent=2):
    return base ** exponent

print(power(3))     # 9  (exponent defaults to 2)
print(power(3, 3))  # 27

Keyword Arguments

Call a function by naming the parameters explicitly. Order does not matter.

def describe(name, age, city):
    return f"{name}, age {age}, from {city}"

print(describe(age=16, city="Mumbai", name="Leo"))

Positional-Only and Keyword-Only Arguments

def greet(name, /, *, greeting="Hello"):
    # name is positional-only (before /)
    # greeting is keyword-only (after *)
    return f"{greeting}, {name}!"

print(greet("Asha"))                  # Hello, Asha!
print(greet("Asha", greeting="Hi"))   # Hi, Asha!

*args — Variable Positional Arguments

Accept any number of positional arguments as a tuple:

def add_all(*numbers):
    return sum(numbers)

print(add_all(1, 2, 3))        # 6
print(add_all(10, 20, 30, 40)) # 100

**kwargs — Variable Keyword Arguments

Accept any number of keyword arguments as a dictionary:

def profile(**info):
    for key, value in info.items():
        print(f"{key}: {value}")

profile(name="Mina", age=15, hobby="coding")

Combining All Argument Types

The order is: positional, *args, keyword-only, **kwargs:

def example(a, b, *args, keyword=True, **kwargs):
    print(a, b, args, keyword, kwargs)

Scope: Local vs Global Variables

A variable created inside a function is local to that function:

def my_function():
    local_var = "I only exist here"
    print(local_var)

my_function()
# print(local_var)  # NameError — not accessible here

A variable defined outside functions is global:

app_name = "Nandhoo"

def show_name():
    print(app_name)  # can read a global variable

show_name()

Use global to modify a global variable from inside a function (use sparingly):

counter = 0

def increment():
    global counter
    counter += 1

increment()
print(counter)   # 1

Lambda Functions (Anonymous Functions)

A lambda is a small, single-expression function without a name.

square = lambda x: x ** 2
print(square(5))   # 25

# Often used with built-in functions:
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort(key=lambda x: -x)   # sort descending
print(numbers)   # [9, 5, 4, 3, 1, 1]

Docstrings

Document what a function does using a docstring — the first string in the body:

def calculate_area(radius):
    """
    Calculate the area of a circle.

    Args:
        radius (float): The radius of the circle.

    Returns:
        float: The area of the circle.
    """
    import math
    return math.pi * radius ** 2

Access it with help(calculate_area) or calculate_area.__doc__.

Higher-Order Functions

Functions can accept other functions as arguments or return them.

map() — Transform Each Item

numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled)   # [2, 4, 6, 8, 10]

filter() — Keep Items That Match

numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)   # [2, 4, 6]

Returning a Function

def make_multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

triple = make_multiplier(3)
print(triple(10))   # 30

Recursion

A function that calls itself is recursive. It must have a base case to stop.

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

print(factorial(5))   # 120

Common Mistakes

  • forgetting return (function returns None silently)
  • using mutable default arguments like def func(items=[]) — use None instead
  • modifying a global variable without global keyword (causes UnboundLocalError)
  • very long functions with too many responsibilities (prefer small, focused functions)

Mini Exercises

  1. Write a function is_even(n) that returns True if n is even.
  2. Write a function celsius_to_fahrenheit(c) with the formula (c * 9/5) + 32.
  3. Write a function using *args that returns the average of all arguments.
  4. Write a function that takes another function as an argument and calls it.
  5. Write a recursive function to compute the nth Fibonacci number.

Review Questions

  1. What is the difference between a parameter and an argument?
  2. What does a function return when it has no return statement?
  3. What is the difference between *args and **kwargs?
  4. When would you use a lambda function instead of a regular function?
  5. What is a docstring and why should you write one?

Reference Checklist

  • I can define functions with def and call them
  • I understand positional, keyword, default, *args, and **kwargs
  • I understand local vs global scope
  • I can write and use lambda functions
  • I can write recursive functions with a base case
  • I can write proper docstrings