⚙️ Chapter 7: Functions

This chapter covers functions, reusable blocks of code that perform specific tasks. Functions help you organize code, avoid repetition, and make your programs more readable and maintainable.

6.2 Functions

Functions are like mini-programs within your program. They take input (parameters), perform operations, and can return output (return values).

Defining Functions

def Statement

You define a function using the def keyword:

def greet():
    print("Hello, world!")

greet()  # Call the function

Function Parameters

Functions with Parameters

Parameters allow functions to accept input:

def greet_person(name):
    print(f"Hello, {name}!")

greet_person("Alice")
greet_person("Bob")

Multiple Parameters

def add_numbers(a, b):
    return a + b

result = add_numbers(3, 5)
print(result)  # 8

Default Parameters

You can provide default values for parameters:

def greet_person(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet_person("Alice")  # Hello, Alice!
greet_person("Bob", "Hi")  # Hi, Bob!

Return Values

Returning Values

The return statement sends a value back to the caller:

def square(number):
    return number * number

result = square(4)
print(result)  # 16

Multiple Return Values

Functions can return multiple values as a tuple:

def divide_and_remainder(dividend, divisor):
    quotient = dividend // divisor
    remainder = dividend % divisor
    return quotient, remainder

q, r = divide_and_remainder(17, 5)
print(f"Quotient: {q}, Remainder: {r}")  # Quotient: 3, Remainder: 2

Function Scope

Local vs Global Variables

Variables defined inside a function are local to that function:

global_variable = "Accessible everywhere"

def my_function():
    local_variable = "Only accessible inside this function"
    print(local_variable)  # Works
    print(global_variable)  # Works

my_function()
print(global_variable)  # Works
# print(local_variable)  # Error - not defined

Documentation with Docstrings

Function Documentation

Docstrings document what your function does:

def calculate_area(length, width):
    """
    Calculate the area of a rectangle.
    
    Parameters:
    length (float): The length of the rectangle
    width (float): The width of the rectangle
    
    Returns:
    float: The area of the rectangle
    """
    return length * width

Function Examples

Simple Calculator Functions

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    if y != 0:
        return x / y
    else:
        return "Error: Division by zero"

# Using the functions
result = add(10, 5)
print(f"10 + 5 = {result}")

result = multiply(3, 4)
print(f"3 * 4 = {result}")

Factorial Function

def factorial(n):
    """
    Calculate the factorial of a number using recursion.
    
    Parameters:
    n (int): A non-negative integer
    
    Returns:
    int: The factorial of n
    """
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n-1)

print(factorial(5))  # 120
print(factorial(0))  # 1

Lambda Functions

Anonymous Functions

Lambda functions are small, anonymous functions:

# Regular function
def square(x):
    return x ** 2

# Same function as lambda
square_lambda = lambda x: x ** 2

print(square(5))         # 25
print(square_lambda(5))  # 25

# Lambda with multiple parameters
add = lambda x, y: x + y
print(add(3, 4))  # 7

Built-in Functions

Useful Built-in Functions

  • len(sequence): Returns the length of a sequence
  • max(iterable): Returns the maximum value
  • min(iterable): Returns the minimum value
  • sum(iterable): Returns the sum of elements
  • sorted(iterable): Returns a sorted list
  • type(object): Returns the type of an object

Review Exercises (Chapter 11: Classes and Objects)

  1. Create a Dog class with attributes name and age, and a method bark() that prints "Woof!".
  2. Create a Car class with attributes make, model, and year, and a method start() that prints "Engine started".
  3. Create a Person class with methods eat() and sleep(). Create a Student subclass that inherits from Person and adds a study() method.

Advanced Function Concepts

Recursive Functions

Recursion occurs when a function calls itself. It's useful for problems that can be broken down into smaller, similar problems:

def factorial(n):
    """Calculate factorial using recursion."""
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n-1)

def fibonacci(n):
    """Calculate nth Fibonacci number."""
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(factorial(5))  # 120
print(fibonacci(7))  # 13

Note: Recursion can be elegant but may cause stack overflow for large inputs. For deep recursion, consider iterative approaches.

Function Decorators

Decorators modify the behavior of functions without changing their code:

def uppercase_result(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        if isinstance(result, str):
            return result.upper()
        return result
    return wrapper

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

print(greet("Alice"))  # HELLO, ALICE!

*args and **kwargs

Use *args for variable positional arguments and **kwargs for keyword arguments:

def flexible_function(*args, **kwargs):
    print("Positional args:", args)
    print("Keyword args:", kwargs)

flexible_function(1, 2, 3, name="Alice", age=30)
# Positional args: (1, 2, 3)
# Keyword args: {'name': 'Alice', 'age': 30}

Debugging Functions

Common Function Bugs

  • Forgotten return: Function returns None instead of intended value
  • Infinite recursion: Recursive function without proper base case
  • Scope confusion: Using wrong variable scope
  • Immutable arguments: Trying to modify immutable parameters

Debugging Techniques

import pdb

def problematic_function(n):
    pdb.set_trace()  # Start debugger here
    if n > 5:
        result = n * 2
    else:
        result = n + 10
    return result

# Using print for simple debugging
def debug_multiply(a, b):
    print(f"Debug: a={a}, b={b}")
    result = a * b
    print(f"Debug: result={result}")
    return result

Chapter Summary

This chapter covered functions in detail, including creation, parameters, return values, scope, documentation, recursion, decorators, and debugging techniques. Functions are essential for writing readable, maintainable, and reusable code.

Interactive Quiz: This chapter comes with a free online quiz to test your understanding of Python functions.

100%