23. Functions - basics#

Author: Tue Nguyen

23.1. Outline#

  • Overview

  • Define and call a function

  • Parameters vs. arguments

  • Default arguments

23.2. Overview#

  • Roughly speaking, a function takes some inputs, does something with them, and spits out an output

  • Benefits

    • We define the logic once and reuse it as many times as we want

    • Easier to maintain and improve because the logic is in a single place

    • All lower-level details are abstracted away, making code more succinct and readable

  • Example

    • Suppose you have to frequently extract numbers from text strings

    • Instead of duplicating your code (potentially many lines) every time you do such a task, you can wrap your code into a function

    • When you need to extract numbers from a text string, you just need to call the function (one line)

    • Or when you need to improve your code, you have to make changes in one place (where the function is defined)

23.3. Define and call a function#

  • A function must be defined before being used

  • We use def to define a function as follows

    def function_name(params):
      # Function body here
    
  • Parameters (or params) are the inputs to the function

  • Parameters are optional (a function might have no parameter)

  • The function name follows the rules and conventions introduced in the earlier lessons

  • The function body contains statements that handle some logic

  • A function might explicitly return a value to where it is called using the keyword return

  • Otherwise, None will be returned by default

  • Defining a function means writing code that specify

    • its name

    • parameters that it possibly take

    • what it does to the parameters

    • what value it returns

  • Calling a function means evoke the function to do something

  • Defining vs. calling

    • We define a function only once, but we can call it as many times as we want

    • We use def when define a function, but not when we call it

    • We cannot call a function that has not been defined yet

    • When defining a function, the inputs in the parentheses are called parameters

    • When calling a function, the inputs are called arguments

  • A function can

    • take no param, one params, or many params

    • explicitly return a value using return or implicitly return None

23.3.1. No param, no return#

A function can have no params and returns nothing

a) Ex 1: define a function

# Define a function that prints out "Hi user"
def say_hi():
    print("Hi user")

Comments

  • The function name is say_hi

  • It takes has no params (empty parentheses)

  • It also returns nothing (no keyword return). Thus a None will be return by default

  • What it does is just prints out the line "Hi user"

b) Ex 2: call a function

# Now we call say_hi function defined above
say_hi()
Hi user

Comments

  • We see "Hi user" printed out as expected

  • However, do not confuse this printed text with the returned value

  • This function return None by default

  • To see this, try to assign this function call to a variable

# You see, the text "Hi user" is still printed out
x = say_hi()
Hi user
# Now verify that x is indeed None
print(x)
None

23.3.2. Has params, no return#

A function can have one or many params (separated by commas) and returns nothing

a) Ex 1: one param, no return

# Define a function that takes a name and say hi to that name
def say_hi(name):
    print(f"Hi {name}")
# Call 1
say_hi("Anna")
Hi Anna
# Call 2
say_hi("Bob")
Hi Bob

b) Ex 2: many params, no return

# Define a function that takes first_name, last_name, and sex
# then print out a greeting
def say_hi(first_name, last_name, sex):
    # Specify title
    if sex == "M":
        title = "Mr."
    elif sex == "F":
        title = "Ms."
    else:
        title = "Mr./Ms."
        
    # Say hi
    print(f"Hi {title} {first_name} {last_name}")
# Call 1
say_hi("Bob", "Dylan", "M")
Hi Mr. Bob Dylan
# Call 2
say_hi("Sia", "Kate", "F")
Hi Ms. Sia Kate
# Call 3
say_hi("Johan", "Smith", "N/A")
Hi Mr./Ms. Johan Smith

23.3.3. Has params, has return#

A function can have params and explicitly returns a value

a) Ex 1: add two numbers

# Define a function that takes 2 number and returns their sum
def add(a, b):
    return a + b
# Call 1
add(2, 3)
5
# Call 2
add(-10, 20)
10

b) Ex 2: filter bad customer

# Define a function that take a list of customers (cust)
# another list of bad customers (bad_cust)
# and return those who are in cust but not in bad_cust
def filter_bad_cust(cust, bad_cust):
    return [c for c in cust if c not in bad_cust]
# Init cust and bad_cust
cust = ["Jack", "Bob", "Anna", "Tom", "Andrew"]
bad_cust = ["Anna", "Bob"]

# Call the function
good_cust = filter_bad_cust(cust, bad_cust)

# Check the result
good_cust
['Jack', 'Tom', 'Andrew']

c) Ex 3: return more than one value

  • Python does NOT allow for returning multiple values

  • However, you can use wrap the values you want to return in a collection such as a list or tuple, and return that collection

# Define a function that return the perimeter and area of a rectangle
def compute_rect_peri_area(a, b):
    peri = (a + b) * 2
    area = a * b
    return peri, area
# Call 1: get a tuple back
compute_rect_peri_area(2, 3)
(10, 6)
# Call 2: unpack the result
p, a = compute_rect_peri_area(2, 3)
print(p)
print(a)
10
6

23.4. Params vs. arguments#

  • Params are named variables appearing in the function’s signature when we define a function

    • They specify inputs that the function expects to receive when it is actually called

    • The function uses the params inside its body as regular variables

  • Arguments are actual values that are passed to the function when we call it

    • The order in which we pass the arguments must match that of the params

    • When we call a function and supply arguments, Python will assign the value of the arguments to the corresponding parameters and use those values to perform the logic inside the function body

  • For now, if a function has N params, we must supply N arguments when calling it

a) Ex 1: params

# Define a funtion
def say_hi(first_name, last_name):
    print(f"Hi {first_name} {last_name}")

Comments

  • The say_hi function has 2 params first_name and last_name

  • These two params are used in the print statement inside the function body

b) Ex 2: argument

# Call 1
first = "John"
last = "Doe"
say_hi(first, last)
Hi John Doe

Comments

  • In the above function call, we pass two variables as the arguments to say_hi function

    • first as the first argument

    • last as the second argument

  • Based on the order in the function’s signature in Ex 1, Python does the following

    • assign the value of first (which is "John") to param first_name

    • assign the value of last (which is "Doe") to param last_name

    • then execute the body using those values

    • and we see "Hi John Doe" printed out

# Call 2
say_hi("John", "Doe")
Hi John Doe

Comments

  • Similar to Call 1, here we pass "John" and "Doe" to say_hi directly

  • The function uses those values inside its body and we see the same result printed out

c) Ex 3: not enough arguments

  • Try to say_hi("John") or say_hi("Doe") and you will get an error

  • The error looks like this say_hi() missing 1 required positional argument: 'last_name'

  • This means that

    • The function say_hi expects 2 arguments, but you supplied only one

    • Based on the order, this one will goes to the first param (first_name)

    • Thus, you missed the argument for the second param (last_name)

23.5. Default arguments#

  • Parameters allow us to customize the behavior of a function

  • The more params, the more flexible we have

  • However, more parameters also cause more problems

    • Suppose the function has 10 params, then we have to supply 10 arguments in every call

    • We also have to remember the correct order when passing the arguments

  • One solution is to use default values for some parameters to avoid passing too many arguments in each call

    • The default values are set when we define the function

    • In a function call, if we don’t supply the corresponding argument, then the default values will be used instead

    • These default values are called default arguments

    • Note that all parameters that have default values must be specified to the rightmost of the param list in the function’s signature

a) Ex 1: greeting

  • Suppose we want to say hi to a user using his/her username

  • If the user has logged in, then we want to print out "Hi {username}"

  • However, if the user has not logged in (we do not know the user name), then we want to print out "Hi user"

  • A good convention: unlike in assignments, there is no space around = in the function signature

# Define the function
def say_hi(username="user"):
    print(f"Hi {username}")
# Call 1: if we don't supply the argument for the username
# then say_hi will use the value "user"
# We will not get an error
say_hi()
Hi user
# Call 1: if we do supply the argument for username
# then say_hi will use the value we provided
say_hi("Tom")
Hi Tom

b) Ex 2: make a student record

  • Suppose we want to write a function that take the following information from a student

    • Name

    • ID

    • Level (BS, MS, or PhD)

    • Country (Italy, US, …)

  • The function will return those in a dictionary of 4 elements

  • Suppose that for some reason, most of the students are undergraduates (level="BS") and from Italy (country="Italy")

  • Thus, we can set default arguments for level and country to reduce unnecessary manual work

# Define the function
def get_student_info(name, std_id, level="BS", country="Italy"):
    return {
        "name": name, 
        "id": std_id, 
        "level": level, 
        "country": country
    }
# Call 1: Alessandro with ID 220001 from Italy studying BS
get_student_info("Alessandro", 220001)
{'name': 'Alessandro', 'id': 220001, 'level': 'BS', 'country': 'Italy'}
# Call 2: Giovanni with ID 220002 from Italy studying MS
get_student_info("Alessandro", 220002, "MS")
{'name': 'Alessandro', 'id': 220002, 'level': 'MS', 'country': 'Italy'}
# Call 3: John with ID 220003 from US studying BS
# This time, since country is specified after level,
# you have to pass "US" as a keyword argument using country="BS"
# If you pass ("Alessandro", 220001, "US") 
# then Python will understand "US" is the value passed to level
get_student_info("Alessandro", 22003, country="US")
{'name': 'Alessandro', 'id': 22003, 'level': 'BS', 'country': 'US'}
# Call 4: Tue with ID 220004 from Vietnam studying MS
get_student_info("Tue", 22004, "MS", "Vietnam")
{'name': 'Tue', 'id': 22004, 'level': 'MS', 'country': 'Vietnam'}
# Call 5: if using keyword arguments
# you can swap the order of default arguments
get_student_info("Tue", 22004, country="Vietnam", level="MS")
{'name': 'Tue', 'id': 22004, 'level': 'MS', 'country': 'Vietnam'}

23.6. Summary#

Overview

  • Roughly speaking, a function takes some inputs, does something with them, and spits out an output

  • Benefits

    • We define the logic once and reuse it as many times as we want

    • Easier to maintain and improve because the logic is in a single place

    • All lower-level details are abstracted away, making code more succinct and readable

Define and call a function

  • A function must be defined before being used

  • We use def to define a function as follows

    def function_name(params):
      # Function body here
    
  • Parameters (or params) are the inputs to the function

  • Parameters are optional (a function might have no parameter)

  • The function name follows the rules and conventions introduced in the earlier lessons

  • The function body contains statements that handle some logic

  • A function might explicitly return a value to where it is called using the keyword return

  • Otherwise, None will be returned by default

  • Defining a function means writing code that specify

    • its name

    • parameters that it possibly take

    • what it does to the parameters

    • what value it returns

  • Calling a function means evoking the function to do something

  • Defining vs. calling

    • We define a function only once, but we can call it as many times as we want

    • We use def when define a function, but not when we call it

    • We cannot call a function that has not been defined yet

    • When defining a function, the inputs in the parentheses are called parameters

    • When calling a function, the inputs are called arguments

  • A function can

    • take no param, one param, or many params

    • explicitly return a value using return or implicitly return None

Params vs. arguments

  • Params are named variables appearing in the function’s signature when we define a function

  • Arguments are actual values that are passed to the function when we call it

  • When passing arguments and the number of arguments and the order they are passed must match that of parameters

Default arguments

  • We can set default values to some parameters to avoid passing unnecessary information

  • Syntax

    def f(a1, a2, ...., b1=v1, b2=v2, ...):
        # Body
    
  • Params with default values must be specified to the rightmost of the param list

  • A good convention: there is no space around = in the function signature

  • You can use keyword arguments to skip passing arguments for b1. Ex f(a1, a2, ..., b2=v2)

  • When passing arguments using keyword arguments, you can swap the order of default arguments. Ex: f(a1, a2, ..., b2=v2, b1=v1)