Skip to content

Functions ​

Learn to create reusable code blocks that make your programs modular, organized, and easier to maintain

πŸ“ Function Definition ​

Functions are like recipes - they take ingredients (parameters), follow steps (code), and produce a result (return value).

Think of it like this: Instead of writing the same cooking instructions every time, you create a recipe once and use it whenever needed.

Basic Function ​

python
def greet():
    """A simple function that prints a greeting."""
    print("Hello, World!")
    print("Welcome to Python programming!")

# Call the function
greet()

# Expected output:
# Hello, World!
# Welcome to Python programming!

Function with Parameters ​

python
def greet(name):
    """Greet a person by name."""
    print(f"Hello, {name}!")
    print(f"Nice to meet you, {name}!")

# Call with different arguments
greet("Alice")
greet("Bob")

# Expected output:
# Hello, Alice!
# Nice to meet you, Alice!
# Hello, Bob!
# Nice to meet you, Bob!

πŸ”„ Return Values ​

Functions can give back results using the return statement - like a vending machine that takes your money and gives you a snack.

Function with Return Value ​

python
def add(x, y):
    """Add two numbers and return the result."""
    result = x + y
    print(f"Adding {x} + {y}")
    return result

def multiply(x, y):
    """Multiply two numbers and return the result."""
    return x * y

# Use return values
sum_result = add(5, 3)
print(f"Sum: {sum_result}")

product = multiply(4, 6)
print(f"Product: {product}")

# Chain function calls
final_result = add(multiply(2, 3), 4)
print(f"Final result: {final_result}")

# Expected output:
# Adding 5 + 3
# Sum: 8
# Product: 24
# Adding 6 + 4
# Final result: 10

πŸ”§ Function Parameters ​

Think of parameters like settings on a washing machine - you can adjust them for different situations.

Default Parameters ​

python
def greet(name, greeting="Hello", punctuation="!"):
    """Greet someone with customizable greeting and punctuation."""
    message = f"{greeting}, {name}{punctuation}"
    print(message)
    return message

# Using defaults
greet("Alice")

# Overriding some defaults
greet("Bob", "Hi")

# Overriding all defaults
greet("Charlie", "Hey", "!!!")

# Expected output:
# Hello, Alice!
# Hi, Bob!
# Hey, Charlie!!!

Keyword Arguments ​

python
def describe_person(name, age, city, hobby="reading"):
    """Describe a person with their details."""
    description = f"{name} is {age} years old, lives in {city}, and enjoys {hobby}"
    print(description)
    return description

# Positional arguments
describe_person("Alice", 25, "New York")

# Keyword arguments (order doesn't matter)
describe_person(city="Boston", name="Bob", age=30, hobby="cooking")

# Mixed arguments
describe_person("Charlie", age=35, city="Chicago")

# Expected output:
# Alice is 25 years old, lives in New York, and enjoys reading
# Bob is 30 years old, lives in Boston, and enjoys cooking
# Charlie is 35 years old, lives in Chicago, and enjoys reading

Variable Arguments (*args) ​

*Think of args like a shopping bag - you can put as many items as you want inside.

python
def sum_all(*numbers):
    """Sum any number of arguments."""
    print(f"Received numbers: {numbers}")
    total = 0
    for number in numbers:
        total += number
    print(f"Sum: {total}")
    return total

def find_maximum(*values):
    """Find the maximum value among any number of arguments."""
    if not values:
        return None
    
    max_val = values[0]
    for value in values:
        if value > max_val:
            max_val = value
    
    print(f"Maximum of {values} is {max_val}")
    return max_val

# Test with different numbers of arguments
sum_all(1, 2, 3)
sum_all(1, 2, 3, 4, 5)
sum_all(10, 20)

find_maximum(5, 2, 8, 1, 9)
find_maximum(100)

# Expected output:
# Received numbers: (1, 2, 3)
# Sum: 6
# Received numbers: (1, 2, 3, 4, 5)
# Sum: 15
# Received numbers: (10, 20)
# Sum: 30
# Maximum of (5, 2, 8, 1, 9) is 9
# Maximum of (100,) is 100

Keyword Variable Arguments (**kwargs) ​

**Think of kwargs like a form with multiple fields - you can pass any number of named pieces of information.

python
def print_info(**kwargs):
    """Print information passed as keyword arguments."""
    print("Person Information:")
    for key, value in kwargs.items():
        print(f"  {key}: {value}")
    print()  # Empty line for separation

def create_profile(name, **additional_info):
    """Create a user profile with required name and optional additional info."""
    profile = {"name": name}
    profile.update(additional_info)
    
    print(f"Created profile for {name}:")
    for key, value in profile.items():
        print(f"  {key}: {value}")
    
    return profile

# Test kwargs
print_info(name="Alice", age=25, city="New York", hobby="photography")
print_info(username="bob123", email="bob@email.com", role="admin")

# Test mixed parameters
alice_profile = create_profile("Alice", age=25, city="Boston", job="Engineer")

# Expected output:
# Person Information:
#   name: Alice
#   age: 25
#   city: New York
#   hobby: photography
#
# Person Information:
#   username: bob123
#   email: bob@email.com
#   role: admin
#
# Created profile for Alice:
#   name: Alice
#   age: 25
#   city: Boston
#   job: Engineer

🌍 Function Scope ​

Variable scope is like room access in a house - some variables are private to a room (local), while others are shared in common areas (global).

python
# Global variable (accessible everywhere)
global_message = "I'm accessible everywhere!"

def demonstrate_scope():
    """Show how variable scope works."""
    # Local variable (only accessible inside this function)
    local_message = "I'm only accessible inside this function!"
    
    print("Inside function:")
    print(f"  Global: {global_message}")
    print(f"  Local: {local_message}")

def modify_global():
    """Show how to modify global variables."""
    global global_message
    old_message = global_message
    global_message = "I've been modified!"
    
    print(f"Changed global message from '{old_message}' to '{global_message}'")

# Test scope
print("Outside function:")
print(f"  Global: {global_message}")

demonstrate_scope()
modify_global()

print("\nAfter modification:")
print(f"  Global: {global_message}")

# Expected output:
# Outside function:
#   Global: I'm accessible everywhere!
# Inside function:
#   Global: I'm accessible everywhere!
#   Local: I'm only accessible inside this function!
# Changed global message from 'I'm accessible everywhere!' to 'I've been modified!'
# 
# After modification:
#   Global: I've been modified!

🎯 Lambda Functions ​

Lambda functions are like shortcuts - they're quick, one-line functions for simple tasks.

Basic Lambda ​

python
# Regular function
def square(x):
    """Square a number using a regular function."""
    result = x ** 2
    print(f"Square of {x} is {result}")
    return result

# Lambda function (one-liner)
square_lambda = lambda x: x ** 2

# Test both approaches
print("Regular function:")
regular_result = square(5)

print("\nLambda function:")
lambda_result = square_lambda(5)
print(f"Lambda result: {lambda_result}")

# Expected output:
# Regular function:
# Square of 5 is 25
# 
# Lambda function:
# Lambda result: 25

Lambda with Multiple Arguments ​

python
# Lambda functions with multiple parameters
add = lambda x, y: x + y
multiply = lambda x, y, z: x * y * z
greet = lambda name: f"Hello, {name}!"

print(f"Addition: {add(3, 5)}")
print(f"Multiplication: {multiply(2, 3, 4)}")
print(f"Greeting: {greet('Alice')}")

# Using lambda with built-in functions
numbers = [1, 2, 3, 4, 5]
print(f"Original numbers: {numbers}")

squared = list(map(lambda x: x**2, numbers))
print(f"Squared: {squared}")

even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Even numbers: {even_numbers}")

# Expected output:
# Addition: 8
# Multiplication: 24
# Greeting: Hello, Alice!
# Original numbers: [1, 2, 3, 4, 5]
# Squared: [1, 4, 9, 16, 25]
# Even numbers: [2, 4]

πŸš€ Practical Examples ​

Example 1: Calculator Functions ​

python
def calculator():
    """A simple calculator with multiple operations."""
    
    def add(x, y):
        result = x + y
        print(f"{x} + {y} = {result}")
        return result
    
    def subtract(x, y):
        result = x - y
        print(f"{x} - {y} = {result}")
        return result
    
    def multiply(x, y):
        result = x * y
        print(f"{x} Γ— {y} = {result}")
        return result
    
    def divide(x, y):
        if y != 0:
            result = x / y
            print(f"{x} Γ· {y} = {result}")
            return result
        else:
            print("Error: Cannot divide by zero!")
            return None
    
    # Test all operations
    print("Calculator Demo:")
    add(10, 5)
    subtract(10, 5)
    multiply(10, 5)
    divide(10, 5)
    divide(10, 0)  # Test error handling

# Run calculator demo
calculator()

# Expected output:
# Calculator Demo:
# 10 + 5 = 15
# 10 - 5 = 5
# 10 Γ— 5 = 50
# 10 Γ· 5 = 2.0
# Error: Cannot divide by zero!

Example 2: Temperature Converter ​

python
def temperature_converter():
    """Convert temperatures between Celsius and Fahrenheit."""
    
    def celsius_to_fahrenheit(celsius):
        fahrenheit = (celsius * 9/5) + 32
        print(f"{celsius}Β°C = {fahrenheit}Β°F")
        return fahrenheit
    
    def fahrenheit_to_celsius(fahrenheit):
        celsius = (fahrenheit - 32) * 5/9
        print(f"{fahrenheit}Β°F = {celsius:.1f}Β°C")
        return celsius
    
    def convert_temperature(temp, from_scale):
        """Universal temperature converter."""
        if from_scale.lower() == 'c':
            return celsius_to_fahrenheit(temp)
        elif from_scale.lower() == 'f':
            return fahrenheit_to_celsius(temp)
        else:
            print(f"Error: Invalid scale '{from_scale}'. Use 'C' or 'F'.")
            return None
    
    # Test conversions
    print("Temperature Converter Demo:")
    convert_temperature(100, 'c')    # Boiling point of water
    convert_temperature(32, 'f')     # Freezing point of water
    convert_temperature(98.6, 'f')   # Body temperature
    convert_temperature(0, 'c')      # Freezing point
    convert_temperature(25, 'x')     # Invalid scale

# Run temperature converter demo
temperature_converter()

# Expected output:
# Temperature Converter Demo:
# 100Β°C = 212.0Β°F
# 32Β°F = 0.0Β°C
# 98.6Β°F = 37.0Β°C
# 0Β°C = 32.0Β°F
# Error: Invalid scale 'x'. Use 'C' or 'F'.

Example 3: Password Validator ​

python
def create_password_validator():
    """Create a comprehensive password validation system."""
    
    def check_length(password, min_length=8):
        """Check if password meets minimum length requirement."""
        return len(password) >= min_length
    
    def check_character_types(password):
        """Check for different character types in password."""
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)
        
        return has_upper, has_lower, has_digit, has_special
    
    def validate_password(password):
        """Comprehensive password validation."""
        print(f"Validating password: {'*' * len(password)}")
        
        # Check length
        if not check_length(password):
            return False, "❌ Password must be at least 8 characters long"
        
        # Check character types
        has_upper, has_lower, has_digit, has_special = check_character_types(password)
        
        issues = []
        if not has_upper:
            issues.append("uppercase letter")
        if not has_lower:
            issues.append("lowercase letter")
        if not has_digit:
            issues.append("digit")
        if not has_special:
            issues.append("special character")
        
        if issues:
            return False, f"❌ Password must contain: {', '.join(issues)}"
        
        return True, "βœ… Password is strong and valid!"
    
    # Test different passwords
    test_passwords = [
        "weak",
        "weakpassword",
        "WeakPassword",
        "WeakPassword1",
        "StrongPass123!",
        "MySecure@Pass2024"
    ]
    
    print("Password Validator Demo:")
    for password in test_passwords:
        is_valid, message = validate_password(password)
        print(f"  {message}")
        print()

# Run password validator demo
create_password_validator()

# Expected output:
# Password Validator Demo:
# Validating password: ****
#   ❌ Password must be at least 8 characters long
# 
# Validating password: ************
#   ❌ Password must contain: uppercase letter, digit, special character
# 
# Validating password: ************
#   ❌ Password must contain: digit, special character
# 
# Validating password: *************
#   ❌ Password must contain: special character
# 
# Validating password: **************
#   βœ… Password is strong and valid!
# 
# Validating password: ******************
#   βœ… Password is strong and valid!

πŸ“š Documentation with Docstrings ​

Docstrings are like instruction manuals - they explain what your function does, what it needs, and what it returns.

python
def calculate_circle_properties(radius):
    """
    Calculate various properties of a circle.
    
    This function demonstrates proper docstring formatting
    and comprehensive documentation practices.
    
    Args:
        radius (float): The radius of the circle in units
        
    Returns:
        dict: A dictionary containing:
            - area (float): The area of the circle
            - circumference (float): The circumference of the circle
            - diameter (float): The diameter of the circle
            
    Raises:
        ValueError: If radius is negative
        
    Example:
        >>> properties = calculate_circle_properties(5)
        >>> print(properties['area'])
        78.54
    """
    if radius < 0:
        raise ValueError("Radius cannot be negative")
    
    import math
    
    properties = {
        'radius': radius,
        'diameter': 2 * radius,
        'circumference': 2 * math.pi * radius,
        'area': math.pi * radius ** 2
    }
    
    # Print results for demonstration
    print(f"Circle with radius {radius}:")
    for key, value in properties.items():
        if isinstance(value, float):
            print(f"  {key}: {value:.2f}")
        else:
            print(f"  {key}: {value}")
    
    return properties

# Test the function
circle = calculate_circle_properties(5)

# Access the docstring
print("\nFunction documentation:")
print(calculate_circle_properties.__doc__)

# Expected output:
# Circle with radius 5:
#   radius: 5
#   diameter: 10
#   circumference: 31.42
#   area: 78.54
# 
# Function documentation:
# [The full docstring would be displayed here]

πŸ”— Next Steps ​


Key Takeaways:

  • Functions are reusable code blocks that make programs modular and organized
  • Parameters make functions flexible; return values provide useful output
  • Variable scope determines where variables can be accessed (local vs global)
  • Lambda functions provide concise syntax for simple operations
  • Docstrings document function behavior and make code maintainable
  • Good function design follows the principle: one function, one purpose

Released under the MIT License.