Functions
CSCI 1470

by K. Yue

1. Functions: Introduction

1.1 Mathematical Functions

Examples:

f(x) = 2 * x + 4
g(y) = y ** 2 + 1
h(x,y,z) = x + y + z

f(1) returns 6
f(3) returns 10
g(2) returns 5
g(4) returns 17
h(1,2,3) returns 6
h(f(1),g(2),f(3)+g(4)) returns 38

1.2 Python Functions

There are many advantages of using Python's functions. Some advantages include:

  1. Code Reuse: a function can be called multiple times by many programs.
  2. Decomposition and modularity: complex code can be decomposed into multiple functions.
  3. Simpler development: a function represents a single well-defined action.
  4. Reliability and error reduction: a function can be thoroughly tested before use.
  5. Improved organization and software architecture: functions can be organized into classes, modules and packages for efficient organization.
  6. Understandability: when there is no side effects, functions can be thoroughly understood by their return values.
  7. Encapsulation: functions encapsulate a specific set of operations, hiding the internal details from the callers.

2. User-defined Functions

def function_name(parameters):
    function_body (indented)

Example:

fun_basic_1.py

def area(width, length):
    print(f"Area of a {width} * {length} rectangle: {width * length}")
    #   The print statement is an example of 'side effect': action other than the return value. Side effects should be avoided as much as possible.
   
area(5,6)
area(7,10)

#   A better version: no side effect and use the return statement. It is also more general and useful.

def area2(width, length):
    return width * length
   
print(f"area2(5,6): {area2(5,6)}")
print(f"area2(7,2*4+2): {area2(7,2*4+2)}")
   
width_1 = 5
length_1 = 6
print(f"Area of a {width_1} * {length_1} rectangle: {area2(width_1, length_1)}")

width_2 =  7
length_2 = 10

print(f"Sum of the areas of the two rectangles: {area2(width_1, length_1) + area2(width_2, length_2)}")

def hello(name):
    """
    hello takes a name as a parameter and prints a greeting.
    """
    print(f"Hello, {name}!")
    print("Nice to meet you.")
   
hello('Bun')
hello('Jane')

The following example shows some advantages of functions.

Example:

Get the names and ages of three players and display greetings.

Without using function: player_greetings_no_fun.py

# Process player 1 input and display a welcome message.
player_1_name = input("Player 1: please enter your name: ")
player_1_age = int(input("Player 1: please enter your age: "))

if player_1_age >= 18:
   print(f"Hello, {player_1_name}! You are an adult.")
else:
   print(f"Hello, {player_1_name}! You are a minor.")
   
# Process player 2 input and display a welcome message.
player_2_name = input("Player 2: please enter your name: ")
player_2_age = int(input("Player 2: please enter your age: "))

if player_2_age >= 18:
   print(f"Hello, {player_2_name}! You are an adult.")
else:
   print(f"Hello, {player_2_name}! You are a minor.")
   
# Process player 3 input and display a welcome message.
player_3_name = input("Player 3: please enter your name: ")
player_3_age = int(input("Player 3: please enter your age: "))

if player_3_age >= 18:
   print(f"Hello, {player_3_name}! You are an adult.")
else:
   print(f"Hello, {player_3_name}! You are a minor.")

 

Using two functions: player_greetings.py

def get_user_details(user):
   name = input(f"{user}: please enter your name: ")
   age = int(input(f"{user}: please your age: "))
   return name, age

def display_greeting(name, age):
   if age >= 18:
      print(f"Hello, {name}! You are an adult.")
   else:
      print(f"Hello, {name}! You are a minor.")

# Main program
player_1_name, player_1_age = get_user_details("Player 1")
display_greeting(player_1_name, player_1_age)

player_2_name, player_2_age = get_user_details("Player 2")
display_greeting(player_2_name, player_2_age)

player_3_name, player_3_age = get_user_details("Player 3")
display_greeting(player_3_name, player_3_age)

 

If the way of getting input and displaying greetings are changed, and there are now four players:

player_greetings_v2.py

def get_user_details(user):
   name = input(f"{user}: please enter your name: ")
   age = int(input(f"{user}:
and your age: "))
   return name, age

def display_greeting(name, age):
   if age >= 90:
      print(f"Hello, {name}!
Sure want to play? :-)")
   else:
      print(f"Hello, {name}!
Let's roll.")

# Main program
player_1_name, player_1_age = get_user_details("Player 1")
display_greeting(player_1_name, player_1_age)

player_2_name, player_2_age = get_user_details("Player 2")
display_greeting(player_2_name, player_2_age)

player_3_name, player_3_age = get_user_details("Player 3")
display_greeting(player_3_name, player_3_age)

player_4_name, player_4_age = get_user_details("Player 4")
display_greeting(player_4_name, player_4_age)

 

2.1 Function parameters and arguments

2.1.1 Positional and named parameters

Example:

param_1.py:

#  positional parameters
def add_two(a,b):
   return a+b
print("""
def add_two(a=1,b=0):
   return a+b
""")  
print(f"add_two(1,4): {add_two(1,4)}")
print(f"add_two(a=1,b=4): {add_two(a=1,b=4)}")
print(f"add_two(-1,40): {add_two(-1,40)}")

def add_two_with_default(a=1,b=2):
   return a+b
print("""
def add_two_with_default(a=1,b=2):
   return a+b
""")  
print(f"add_two_with_default(1,4): {add_two_with_default(1,4)}")
print(f"add_two_with_default(a=1,b=4): {add_two_with_default(a=1,b=4)}")
print(f"add_two_with_default(-1,40): {add_two_with_default(-1,40)}")
print(f"add_two_with_default(8): {add_two_with_default(8)}")
print(f"add_two_with_default(b=1): {add_two_with_default(b=1)}")
print(f"add_two_with_default(): {add_two_with_default()}")

#   Positional parameters and keyworded (named) parameters.
#       A parameter has a position and a name.
def introduce(name, age):
    print(f"My name is {name} and I am {age} years old.")
    return 'done'
   
print("""
def introduce(name, age):
    print(f"My name is {name} and I am {age} years old.")
    return 'done'
""")

print(f"introduce('Jane', 19): {introduce('Jane', 19)}")
print(f"introduce(19,'Jane'): {introduce(19,'Jane')}")
print(f"introduce(name='Jane', age=19): {introduce(name='Jane', age=19)}")
print(f"introduce(age=19,name='Jane'): {introduce(age=19,name='Jane')}")

Example:

param_2.py:

#  arbitrary numbers of arguments.

def add(*args):
   return sum(args)
   
print("""
def add(*args):
   return sum(args)
""")

print(f"add(1,2,3,4,5): {add(1,2,3,4,5)}")
print(f"add(10,2,3,4,5,6,7): {add(10,2,3,4,5,6,7)}")

def multiplied_sum(*numbers, multiplier=1):
    total_sum = sum(numbers)
    return total_sum * multiplier

print("""
def multiplied_sum(*numbers, multiplier=1):
    total_sum = sum(numbers)
    return total_sum * multiplier
""")
print(f"multiplied_sum(1,2,3,4,multiplier=5): {multiplied_sum(1,2,3,4,multiplier=5)}")
print(f"multiplied_sum(1,2,3,4,5): {multiplied_sum(1,2,3,4,5)}")

def area(*, width, length):
    return width * length
print("""
def area(*, width, length):
    return width * length
""")
   
print(f"area(width=2,length=3): {area(width=2,length=3)}")
print(
"""
print(f"area(2,3): {area(2,3)}")
TypeError: area() takes 0 positional arguments but 2 were given
"""
)

2.2 Specifying types of functions

Example:

def sum(a: int, b: int) -> int:
    return a+b
#

def divide(a: int, b: int) -> in | None:
    if b == 0:
        return None
    else:    
        return a+b

2.3 Functions are objects

Example:

fun_obj_1.py: functions are callable objects.

def hello(name):
   print(f"hello, {name}. Nice to see you.")
  
def bye(name):
   print(f"Bye, {name}. See you next time.")  
  
my_func = hello
my_func('Bun')
my_func = bye
my_func('Bun')

greetings = {
    "hello": hello,
    "bye": bye
}

greetings["hello"]("Jane")
greetings["bye"]("Jane")

3. Python built-in functions and imported functions

3.1 Built-in functions

To understand built-in functions:

  1. Consult the official Python documentation: https://docs.python.org/3/library/functions.html
  2. Use the help() function in Python
  3. Textbook, tutorials, courses, instructors, ...

Example:

Some categories of built-in functions:

  1. Type conversions and creation: e.g., int(), float(), str(), list(), tuple(), ...
  2. Mathematical operations: e.g., max(), min(), sum(), pow(), ...
  3. Iteration and sequence operations: e.g., len(), range(), enumerate(), reversed(), sorted(), ...
  4. Object and attribute manipulation operations: e.g., id(), type(), hasattr(), ...
  5. Input/Output: e.g., input(), print(), open(), ...
  6. Code execution operations: e.g., eval(), exec(), ...

3.2 Imported functions

Example:

builtin_fun_1.py:

import math
from random import randint

# imported functions of the math module are called by full names: e.g., math.sqrt()
# imported function randint of the random module is called directly.

print(f"math.sqrt(64): {math.sqrt(64)}")
print(f"math.pi: {math.pi}")
print(f"math.sin(1): {math.sin(1)}")
print(f"math.gcd(24,36,126): {math.gcd(24,36,126)}")
print(f"math.lcm(24,36,126): {math.lcm(24,36,126)}")

print(f"randint(1,10): {randint(1,10)}")
print(f"randint(1,10): {randint(1,10)}")
print(f"randint(1,10): {randint(1,10)}")
print(f"randint(1,10): {randint(1,10)}")
print(f"randint(1,10): {randint(1,10)}")