πŸ“ Return Output using Functions#

The value in functions come from their ability to perform operations on an input and return a new output.

Explicit Return statement#

Aside: Why the number 42?

The number 42 is especially significant to fans of science fiction novelist Douglas Adams’ β€œThe Hitchhiker’s Guide to the Galaxy,” because that number is the answer given by a supercomputer to β€œthe Ultimate Question of Life, the Universe, and Everything.”

def return_42():
    return 42  # An explicit return statement
return_42()
42

You can modify an explicit return statement

return_42() * 2
84
return_42() - 2
40

Functions with Inputs and returns#

def mean(sample):
    return sum(sample) / len(sample)
mean([1, 2, 3, 4])
2.5

Implicit Return Statements#

def add_one(x):
    results = x + 1
value = add_one(5)
value
print(value)
None

Why did this return None?

No return statement was provided thus an implicit return of None was inferred.

Returning multiple values#

You can have a Python function return multiple values

import numpy as np


def statistics(sample):
    
    mean = np.mean(sample)
    median = np.median(sample)
    
    # find unique values in array along with their counts
    vals, counts = np.unique(sample, return_counts=True)

    # find mode
    mode = np.argwhere(counts == np.max(counts))
    return mean, median, mode
mean, median, mode = statistics([0, 1, 2, 3, 3, 4, 5])
print(mean)
print(median)
print(mode)
2.5714285714285716
3.0
[[3]]

Functions calling Functions#

def adder(number, value):
    return number + value


def multiplier(number, factor):
    return number * factor
multiplier(adder(10, 1), 10)
110

Functions by Closure#

Because functions are object you can return a function that is modified by some input

def by_factor(factor):
    def multiply(number):
        return factor * number

    return multiply

Let’s break this down:

  1. You start by calling by_factor with an input of factor.

  1. factor is locally defined so when you run multiply it uses the value for factor

  1. return multiply returns the function with the factor implemented

  1. This function can be used by inputting a number

double = by_factor(2)
double(3)
6
double(12)
24
triple = by_factor(3)
triple(3)
9
triple(12)
36

Advanced Topic (not on exam - maybe bonus): Decorators#

Decorators are tools that take one function as an input and extend its capabilities.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")

    return wrapper


def say_whee():
    print("Whee!")


say_whee = my_decorator(say_whee)
say_whee()
Something is happening before the function is called.
Whee!
Something is happening after the function is called.

Beautiful Python#

Python has a lot of ways to simplify syntax. This might be confusing at first but makes for really nice and simple code

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")

    return wrapper


@my_decorator
def say_whee():
    print("Whee!")
say_whee()
Something is happening before the function is called.
Whee!
Something is happening after the function is called.

Creating decorator files#

You can create Python files, import them, and use them as decorators

%%writefile decorators.py

def do_twice(func):
    def wrapper_do_twice():
        func()
        func()
    return wrapper_do_twice
Overwriting decorators.py
from decorators import do_twice


@do_twice
def say_whee():
    print("Whee!")
say_whee()
Whee!
Whee!

Decorators with Arguments#

from decorators import do_twice


@do_twice
def greet(name):
    print(f"Hello {name}, I am a Drexel Dragon")
greet("Jay")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\Joshua Agar\Documents\codes\Winter_2023_ENGR_131_private\jupyterbook\week4\lecture4\Functions_that_Return.ipynb Cell 47 in <cell line: 1>()
----> <a href='vscode-notebook-cell:/c%3A/Users/Joshua%20Agar/Documents/codes/Winter_2023_ENGR_131_private/jupyterbook/week4/lecture4/Functions_that_Return.ipynb#Y110sZmlsZQ%3D%3D?line=0'>1</a> greet('Jay')

TypeError: wrapper_do_twice() takes 0 positional arguments but 1 was given

The problem is that the inner function wrapper_do_twice() does not take any arguments, but name=”World” was passed to it.

The solution is to use *args and **kwargs in the inner wrapper function. Then it will accept an arbitrary number of positional and keyword arguments.

def do_twice(func):
    def wrapper_do_twice(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    return wrapper_do_twice
@do_twice
def greet(name):
    print(f"Hello {name}, I am a Drexel Dragon")
greet("Jay")
Hello Jay, I am a Drexel Dragon
Hello Jay, I am a Drexel Dragon