Week 9: Functions in Python

CMPSC 100 Computational Expression

Janyl Jumadinova

Functions in Python

Breaking code into reusable pieces

What are Functions?

Functions are reusable blocks of code that perform specific tasks - think of them as “mini-programs” that help organize code and avoid repetition.

You’ve already been using functions in every lab:

print("Hello World")           # print() function
len(my_list)                   # len() function  
range(1, 6)                    # range() function
time.sleep(0.5)                # sleep() function
input("Enter name: ")          # input() function

Every time you use parentheses (), you are calling a function!

Function Anatomy & Example

def function_name(parameters):
    """Optional docstring describing the function"""
    # Function body - the code that runs
    return result  # Optional return value

# Simple example:
def greet():
    """Prints a friendly greeting"""
    print("Hello there!")
    print("Welcome to CS 100!")

greet()  # Call the function

Key parts: def keyword, function name, parameters in (), colon :, indented body, optional return

Functions with Parameters

def greet_person(name):
    """Greets a specific person"""
    print(f"Hello, {name}!")
    print("Welcome to CS 100!")

# Call with different arguments
greet_person("Maya")
greet_person("Carlos")

Key Terms:

  • Parameter: name - the variable in the function definition
  • Argument: "Maya" - the actual value passed when calling the function

Multiple Parameters

def introduce_student(name, major, year):
    """Introduces a student with their info"""
    print(f"Meet {name}!")
    print(f"Major: {major}")
    print(f"Year: {year}")

# Call with multiple arguments
introduce_student("Zara", "Computer Science", "Sophomore")
introduce_student("Kai", "Mathematics", "Junior")

Output:

Meet Zara!
Major: Computer Science
Year: Sophomore
Meet Kai!
Major: Mathematics
Year: Junior

Return Values

Functions can return values back to the caller:

def add_numbers(num1, num2):
    """Adds two numbers and returns the result"""
    return num1 + num2

# Store the returned value
sum_result = add_numbers(5, 3)
print(f"5 + 3 = {sum_result}")

Return vs. Print:

  • return sends a value back for use elsewhere
  • print() displays output but returns None
def print_double(number):
    print(number * 2)  # Displays but doesn't return

def return_double(number):
    return number * 2  # Returns for use elsewhere

Mathematical Functions

def calculate_area(length, width):
    """Calculates the area of a rectangle"""
    return length * width

def grade_to_letter(score):
    """Converts numeric grade to letter grade"""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"

# Using the functions
area = calculate_area(5, 3)
grade = grade_to_letter(85)
print(f"Area: {area}, Grade: {grade}")  # Area: 15, Grade: B

Built-in Math Functions: min() and max()

Python provides useful built-in functions for finding minimum and maximum values:

# Finding min and max of numbers
scores = [78, 92, 85, 69, 94, 88]
highest_score = max(scores)
lowest_score = min(scores)
print(f"Highest: {highest_score}, Lowest: {lowest_score}")  # Highest: 94, Lowest: 69

# Constraining values within bounds (useful for user input validation)
def get_valid_rounds():
    """Gets number of rounds from user, constrained between 3 and 6"""
    rounds = int(input("How many rounds (3-6)? "))
    rounds = max(3, min(6, rounds))  # Ensures rounds is between 3 and 6
    return rounds

# Finding extremes among multiple values
temperature_readings = [72.5, 68.2, 75.1, 70.8, 73.3]
temp_range = max(temperature_readings) - min(temperature_readings)
print(f"Temperature range: {temp_range}°F")  # Temperature range: 6.9°F

Functions with Loops and Lists

def count_down(start):
    """Counts down from start to 1"""
    for i in range(start, 0, -1):
        print(i)
    print("Blast off!")

def sum_list(numbers):
    """Calculates sum of numbers in a list"""
    total = 0
    for num in numbers:
        total = total + num
    return total

# Using the functions
count_down(3)  # Prints: 3, 2, 1, Blast off!
my_numbers = [1, 2, 3, 4, 5]
result = sum_list(my_numbers)
print(f"Sum: {result}")  # Sum: 15

Functions with Random

import random

def roll_dice():
    """Simulates rolling a six-sided die"""
    return random.randint(1, 6)

def create_random_sequence(length):
    """Creates a random sequence of numbers"""
    sequence = []
    for i in range(length):
        number = random.randint(0, 2)  # Random number 0, 1, or 2
        sequence.append(number)
    return sequence

# Using random functions
dice_roll = roll_dice()
random_seq = create_random_sequence(5)
print(f"Dice: {dice_roll}")           # Dice: 4 (random)
print(f"Sequence: {random_seq}")      # Sequence: [1, 0, 2, 1, 0] (random)

Default Parameters

def greet_with_title(name, title="Student"):
    """Greets person with optional title"""
    print(f"Hello, {title} {name}!")

# Call with and without title
greet_with_title("Amara")              # Uses default "Student"
greet_with_title("Dr. Patel", "Professor") # Uses "Professor"

Output:

Hello, Student Amara!
Hello, Professor Dr. Patel!

Function Scope

Local vs Global Variables:

def my_function():
    local_var = "I'm local!"     # LOCAL: Only exists inside function
    print(local_var)

global_var = "I'm global!"       # GLOBAL: Available everywhere

def another_function():
    print(global_var)            # Can access global variables
    # print(local_var)           # Error! Can't access other function's local variables

my_function()        # Prints: I'm local!
another_function()   # Prints: I'm global!

Key Rules:

  • Local variables exist only inside their function
  • Global variables can be accessed from anywhere
  • Each function has its own local scope

Importing Functions

Before using hardware functions, we need to import them:

import time                # Import entire time module
from machine import Pin    # Import specific Pin function from machine module

# Now we can use:
time.sleep(1.0)           # Use sleep function from time module
led = Pin("LED", Pin.OUT)  # Use Pin function we imported

Two ways to import:

  • import module_name - imports the whole module
  • from module_name import function_name - imports specific functions

Good Function Names

# Good - describes what function does
def calculate_tax(price, rate):
    return price * rate

def is_even(number):
    return number % 2 == 0

# Bad - unclear purpose
def func1(x, y):
    return x * y

def check(n):
    return n % 2 == 0

Function Guidelines

  • Each function should do one thing well
  • Keep functions small and focused
  • Use descriptive names and docstrings
  • Test functions with different inputs

Common Function Types

# Validation functions
def is_valid_email(email):
    return "@" in email

def is_positive(number):
    return number > 0

# Conversion functions  
def inches_to_centimeters(inches):
    return inches * 2.54

# Processing functions
def get_initials(first_name, last_name):
    return first_name[0].upper() + last_name[0].upper()

def count_vowels(text):
    vowels = "aeiouAEIOU"
    count = 0
    for char in text:
        if char in vowels:
            count += 1
    return count

Testing Functions

Always test your functions with different inputs:

def is_even(number):
    """Returns True if number is even"""
    return number % 2 == 0

# Test the function
print(is_even(4))    # Should be True
print(is_even(7))    # Should be False
print(is_even(0))    # Should be True
print(is_even(-2))   # Should be True

When to Create Functions

Create a function when you:

  • Repeat code - same logic used multiple times
  • Complex task - break down into smaller pieces
  • Clear purpose - code does one specific thing
  • Reusable logic - might need it in other programs

Summary

Functions help you:

  • Organize code into logical pieces
  • Avoid repetition through reusable code blocks
  • Make debugging easier by isolating problems
  • Improve readability with descriptive function names
  • Enable testing of individual components

The Main Function Pattern

In our labs, we organize code using a main function:

def main():
    """Main function that runs our program"""
    print("Lab 5: Smart Home Monitor")
    user_name = input("Enter name: ")
    active_devices = ["thermostat"]
    # All lab logic goes here

if __name__ == "__main__":
    main()

Why use this pattern?

  • Organizes all program logic in one place
  • Only runs when file is executed directly (not imported)
  • Makes code cleaner and more testable

Understanding __name__

The magic line: if __name__ == "__main__":

# When you run: python main.py
print(__name__)  # Prints: __main__

# When another file imports this file:
import main      # __name__ becomes "main" (the filename)

Python automatically sets __name__:

  • Direct execution: __name__ = "__main__"
  • Imported by another file: __name__ = "filename"

This prevents your main program from running when someone imports your file!