Skip to content

Week 6 - Return Values

Understanding Return Values and Functions in Python

Overview

This lesson focuses on teaching return values in Python functions. It explores how functions return values, conditional returns, recursion with return values, input validation, and debugging. By the end of the lesson, you should have a clear understanding of return values and how to apply them effectively.

Objectives

  • Improve your skill in writing functions that return values.
  • Differentiate between functions that return None and those that return values.
  • Understand how return values work with conditional statements.
  • Explore recursive functions with return values.
  • Apply input validation in functions.
  • Practice incremental development for debugging.

Introduction to Return Values

Concept: As you have probably figured out, a function can return a value that can be assigned to a variable or used in expressions.

Example Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import math

def circle_area(radius):
    area = math.pi * radius ** 2
    return area

# Using return value
radius = math.sqrt(42 / math.pi)
area = circle_area(radius)

# Display result
print(f"Area of circle: {area}")

Key Takeaway: Return values allow functions to pass results back to the caller.


Return Values vs. None

Concept: If a function doesn’t have a return statement, it returns None by default.

Example Code:

1
2
3
4
5
def repeat(word, n):
    print(word * n)

result = repeat('Python, ', 3)
print(f"The return value is: {result}")

Improved Version:

1
2
3
4
5
def repeat_string(word, n):
    return word * n

line = repeat_string('Python, ', 3)
print(f"Repeated string: {line}")

Key Takeaway: Functions without a return statement return None. Always use return for reusable results.


Return Values and Conditionals

Concept: Conditional statements within a function should ensure EVERY possible execution path has a return statement.

Example Code:

1
2
3
4
def absolute_value(x):
    if x < 0:
        return -x
    return x  # NOTE: else is not necessary here, ask yourself why?

Incorrect Code Example:

1
2
3
4
5
6
def absolute_value_wrong(x):
    if x < 0:
        return -x
    if x > 0:
        return x
    # No return for x == 0, returns None unintentionally, so would return None!

Key Takeaway: Make sure every execution path hits a return statement.


Incremental Development

Concept: Write small, testable functions and build them incrementally.

Example Code:

1
2
3
4
5
6
7
8
9
def distance(x1, y1, x2, y2):
    dx = x2 - x1
    dy = y2 - y1
    dsquared = dx**2 + dy**2
    return math.sqrt(dsquared)

# Test the function
d = distance(1, 2, 4, 6)
print(f"Distance: {d}")

Key Takeaway: Build and test small pieces of code to avoid larger debugging issues.


Boolean Functions

Concept: Functions can return True or False based on conditions.

Example Code:

1
2
3
4
5
6
def is_divisible(x, y):
    return x % y == 0  # NOTE: we can return the "boolean expression" without the need for if/else

# Usage
if is_divisible(10, 5):
    print("10 is divisible by 5")

Key Takeaway: Boolean functions simplify complex conditional logic.


Recursion with Return Values

Concept: Recursive functions can use return values to build up solutions.

Example Code:

1
2
3
4
5
6
7
8
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

# Test the function
print(f"Factorial of 5: {factorial(5)}")

Key Takeaway: Recursive functions are powerful, but ensure they have valid base cases to terminate.


Input Validation

Concept: Validate inputs to prevent unexpected behavior.

Example Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def factorial(n:int):  # Using type hints for IDE to verify the parameter
    if not isinstance(n, int) or n < 0:  # Using isinstance to verify the parameter
        return "Error: Input must be a non-negative integer."
    if n == 0:
        return 1
    return n * factorial(n-1)

# Testing input validation
print(factorial(3.5))  # Error
print(factorial(-2))   # Error
print(factorial(5))    # Success

Key Takeaway: Input validation ensures the function works with expected data types and values.


Debugging with Scaffolding

Concept: Use print statements to track flow of execution and return values during debugging.

Example Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def factorial(n):
    space = ' ' * (4 * n)
    print(space, 'factorial', n)

    if n == 0:
        print(space, 'returning 1')
        return 1
    else:
        result = n * factorial(n-1)
        print(space, 'returning', result)
        return result

# Test with tracing
print(f"Result: {factorial(3)}")

Key Takeaway: Use scaffolding to debug complex functions by visualizing intermediate steps.


Exercises

  1. Modify the distance function to calculate the 3D distance between points.
  2. Write a function is_prime(n) that returns True if n is prime and False otherwise.
    • Here’s a basic approach, known as trial division:
      • If the number n is less than or equal to 1, it is not prime.
      • If n is 2 or 3, it is prime.
      • For any number n greater than 3:
        • Check divisibility by 2 or 3 (since primes are not divisible by any number other than 1 and themselves).
      • For other divisors, test up to sqrt(n), because if n is divisible by any number greater than its square root, it must also be divisible by a smaller number.
Answers
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python3
from math import sqrt

def distance(x1, y1, z1, x2, y2, z2):
    dx = x2 - x1
    dy = y2 - y1
    dz = z2 - z1
    dsquared = dx**2 + dy**2 + dz**2
    return sqrt(dsquared)

print(distance(0,0,0, 1, 1, 1))
print(distance(5,10,11, 2, 9, 8))

def is_prime(n, divisor=None):
    if n <= 1:
        return False
    if n == 2 or n == 3:
        return True
    if divisor is None:
        divisor = int(sqrt(n))
    if divisor < 2:
        return True
    if n % divisor == 0:
        return False
    return is_prime(n, divisor - 1)

def is_prime_e(n):
    """ Handling small numbers (less than 4).
        Checking divisibility by 2 and 3.
        Using trial division for other divisors, skipping unnecessary checks by incrementing by 6 (since all primes > 3 are of the form 6k±1).
    """
    if n <= 1:
        return False
    if n == 2 or n == 3:
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False

    # Check divisors up to √n
    for i in range( 5, int( sqrt( n ) ) + 1, 6 ):
        if n % i == 0 or n % (i + 2) == 0:
            return False
    return True

print(is_prime(101))
print(is_prime(100))