Skip to content

Learning Python: Day 19

Chapter 8 – Functions

  • functions – named blocks of code designed to do one job
  • call – used when you want to perform a particular task that you’ve defined in a function
  • call the function to perform the same tasks multiple times
  • modules – separate files that help organize your main program files

Defining a function

  • greet_user() function – greets users

greeter.py

def greet_user():

….”””Display a simple greeting.”””

….print*”Hello!”)

greet_user()

  • def – keyword informs Python that you’re defining a function
  • function definition – tells Python the name of the function and, if applicable, what kind of information the function needs to do its job
  • parentheses contain information Python would need
  • in this case, parentheses are empty because no information is needed to do its job
  • definition ends in a colon
  • body – any indented lines following the definition of a function
  • docstring – text on the second line is a comment that describes what the function does
  • Python looks for the string immediately after the uncitno’s definition when gerating documentation fo the functions
  • these strings are enclosed in triple quotes, which lets you write multiple lines
  • print(“Hello!”) is the only line of code in the body of this function, so greet_user() has one job
  • function call – tells Python to execute the code in the function
  • call a function by writing the name of the function, followed by any necessary information in parentheses

Passing information to a function

  • enter username in the parentheses of the function’s definition at def greet_user() to greet the user by name
  • function expects you to provide a value for username each time you call it
  • when calling greet_user(), you can pass it a name, such as ‘jesse’, inside the parentheses

def greet_user(username):

….”””Display a simple greeting.”””

….print(f”Hello, {username.title()}!””)

greet_user(‘jesse’)

Hello, Jesse!

Arguments and parameters

  • parameter – piece of information the function needs to do its job, such as the variable username in the definition of greet_user()
  • value ‘jesse’ in greet_user(‘jesse’) is an example of an argument
  • argument – a piece of information that’s passed from a function call to a function
  • place value we want to work with in the parentheses

Passing arguments

  • since a function definition can have multiple parameters, a function call may need multiple arguments
  • can use positional arguments, which need to be in the same order parameters were written or keyword arguments, where each argument consists of a variable name and a value, and lists and dictionaries of values

Positional arguments

  • positional arguments – values matched up in the order of arguments provided

pets.py

def describe_pet(animal_type, pet_name):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet(‘hamster’, ‘harry’)

  • definition shows this function needs a type of animal and the animal’s name
  • when calling describe_pet(), we need to provide the info in that order
  • argument ‘hamster’ is assigned to the parameter animal_type
  • argument ‘harry’ is assigned to the parameter pet_name

I have a hamster.

My hamster’s name is Harry.

Multiple function calls

  • can call a function as many times as needed

def describe_pet(animal_type, pet_name):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet(‘hamster’, ‘harry’)

describe_pet(‘dog’, ‘willie’)

I have a hamster.

My hamster’s name is Harry.

I have a dog.

My dog’s name is Willie.

  • calling a function multiple times is a very efficient way to work

Order matters in positional arguments

def describe_pet(animal_type, pet_name):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet(‘harry’, ‘hamster’)

I have a harry.

My harry’s name is Hamster.

  • check order of arguments is correct if you get funny results

Keyword arguments

  • keyword argument – a name-value pair that you pass to a function
  • directly associate the name and the value within the argument
  • don’t need to list them in order

def describe_pet(animal_type, pet_name):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet(animal_type=’hamster’, pet_name=’harry’)

Default values

  • default value – Python will use this if an argument for a parameter is not provided in the function call

def describe_pet(pet_name, animal_type=’dog’):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet(pet_name=’willie’)

  • changed definition of describe_pet() to include the default value, ‘dog’, for animal_type

I have a dog.

My dog’s name is Willie.

  • order of parameters in the function definition had to change because the default value makes it unnecessary to specify a type of animal as an argument
  • the only argument left in the function call is the pet’s name
  • still interpreted as a positional argument, so if the function is called with a pet’s name, the argument will match up with the first parameter

Equivalent function calls

  • since positional arguments, keyword arguments, and default values can all be used together, there are different ways to call a function

def describe_pet(pet_name, animal_type=’dog’):

  • with this definition, an argument always needs to be provided for pet_name, and this value can be provided using the positional or keyword format
  • if the animal being described is not a dog, an argument for animal_type must be included, and it can be specified using the positional or keyword format

# A dog named Willie.

describe_pet(‘willie’)

describe_pet(pet_name=’willie’)

# A hamster named Harry.

describe_pet(‘harry’, ‘hamster’)

describe_pet(pet_name=’harry’, animal_type=’hamster’)

describe_pet(animal_type=’hamster’, pet_name=’harry’)

  • output is the same, so choose your own style

Avoiding arguments errors

  • with functions, unmatched arguments errors are common
  • occur when you provide fewer or more arguments than a function needs to do its work

def describe_pet(animal_type, pet_name):

….”””Display information about a pet.”””

….print(f”\nI have a {animal_type}.”)

….print(f”My {animal_type}’s name is {pet_name.title()}.”)

describe_pet()

Traceback error.

  • missing two arguments
  • give variables and functions descript names so error messages can be more useful

Return values

  • return value – value the function returns
  • allows you to move much of your program’s grunt work into functions

Returning a simple value

formatted_name.py

def get_formatted_name(first_name, last_name):

….”””Return a full name, neatly formatted.”””

….full_name = f”{first_name} {last_name}”

….return full_name.title()

musician = get_formatted_name(‘jimi’, ‘hendrix’)

print(musician)

Jimi Hendrix

  • seems like a lot when you could just do print(“Jimi Hendrix”), but this is useful with a large program that needs to store many first and last names separately, functions like get_formatted_name() become very useful

Make an argument optional

  • makes sense to make an argument optional so people can provide extra information only if they want to
  • for example, expanding get_formatted_name() to handle middle names

def get_formatted_name(first_name, middle_name, last_name):

….”””Return a full name, neatly formatted.”””

….full_name = f”{first_name} {middle_name} {last_name}”

….return full_name.title()

musician = get_formatted_name(‘john’, ‘lee’, ‘hooker’)

print(musician)

John Lee Hooker

  • but middle names aren’t always needed
  • can make the middle name optional by giving the middle_name argument an empty default value and ignore the argument unless the user provides a value

def get_formatted_name(first_name, last_name, middle_name=”):

….”””Return a full name, neatly formatted.”””

….if middle_name:

……..full_name = f”{first_name} {middle_name} {last_name}”

….else:

……..full_name = f”{first_name} {last_name}”

….return full_name.title()

musician = get_formatted_name(‘jimi’, ‘hendrix’)

print(musician)

musician = get_formatted_name(‘john’, ‘hooker’, ‘lee’)

print(musician)

  • since there’s always a first and last name, these parameters are listed first
  • middle name is optional, is listed last, and its default value is an empty string
  • Python interprets non-empty strings as True, so the conditional test if middle_name evaluate to True if a middle name argument is in the functional call

Jimi Hendrix

John Lee Hooker

Returning a dictionary

  • a function can return any kind of value, like complicated data structures such as lists and dictionaries

person.py

def build_person(first_name, last_name):

….”””Return a dictionary of information about a person.”””

….person = {‘first’: first_name, ‘last’: last_name}

….return person

musician = build_person(‘jimi’, ‘hendrix’)

print(musician)

  • function build_person() takes in a first and last name, and puts these values into a dictionary
  • value first_name is stored with the key ‘first’, value last_name is stored with the key ‘last’
  • return value is printed with the original two pieces of textual information now stored in a dictionary

{‘first’: ‘jimi’, ‘last’: ‘hendrix’}

  • can extend function to include middle name, age, occupation, or other information

def build_person(first_name, last_name, age=None):

….”””Return a dictionary of information about a person.”””

….person = {‘first’: first_name, ‘last’: last_name}

….if age:

……..person[‘age’] = age

….return person

musician = build_person(‘jimi’, ‘hendrix’, age = 27)

print(musician)

Using a function with a while loop

  • get_formatted_name() function with a while loop to greet users more formally

greeter.py

def get_formatted_name(first_name, last_name):

….”””Return a full name, neatly formatted.”””

….full_name = f”{first_name} {last_name}”

….return full_name.title()

# This is an infinite loop!

while True:

….print(“\nPlease tell me your name:”)

….f_name = input(“First name:”)

….l_name = input(“Last name:”)

….formatted_name = get_formatted_name(f_name, l_name)

….print(f”\nHello, {formatted_name}!”)

  • problem: we haven’t defined a quit condition for the while loop
  • break statement offers a straightforward way to exit the loop at either prompt

def get_formatted_name(first_name, last_name):

….”””Return a full name, neatly formatted.”””

….full_name = f”{first_name} {last_name}”

….return full_name.title()

while True:

….print(“\nPlease tell me your name:”)

….print(“(enter ‘q’ at any time to quit)”)

….f_name = input(“First name: “)

….if f_name == ‘q’:

……..break

….l_name = input(“Last name: “)

….if l_name == ‘q’:

……..break

….formatted_name = get_formatted_name(f_name, l_name)

….print(f”\nHello, {formatted_name}!”)

  • program will continue greeting people until someone enters q for either name:

Please tell me your name:

(enter ‘q’ at any time to quit)

First name: eric

Last name: matthes

Hello, Eric Matthes!

Please tell me your name:

(enter ‘q’ at any time to quit)

First name: q

Passing a list

  • it’s useful to pass a list to a function, whether it’s a list of names, numbers, or more complex objects, such as dictionaries
  • when passing a list to a function, the function gets direct access to the contents of the list
  • functions make working with lists more efficient
  • for example, if we have a list of users and want to print a greeting to each user

greet_users.py

def greet_users(names):

….”””Print a simple greeting to each user in the list.”””

….for name in names:

……..msg = f”Hello, {name.title()}!”

……..print(msg)

usernames = [‘john’, ‘ty’, ‘mark’]

greet_users(usernames)

  • outside of the function, we define a list of users and then pass the list usernames to greet_users() in the function call

Hello, John!

Hello, Ty!

Hello Mark!

Modifying a list in a function

  • when passing a list to a function, the function can modify the list
  • changes made to the list inside the function’s body are permanent
  • for example, company that creates 3D printed models of designs that users submit
  • designs need to be printed and stored in a list, after being printed they are moved to a separate list
  • following code does this without using functions

printing_models.py

# Start with some designs that need to be printed.

unprinted_designs = [‘phone case’, ‘robot pendant’, ‘dodecahedron’]

completed_models = []

# Simulate printing each design, until none are left.

# Move each design to completed_models after printing.

while unprinted_designs:

….current_design = unprinted_designs.pop()

….print(f”Printing model: {current_design}”)

….completed_models.append(current_design)

# Display all completed models.

print(“\nThe following models have been printed:”)

for completed_model in completed_models:

….print(completed_model)

  • program starts with a list of designs that need to be printed and an empty list called completed_models that each design will be moved to after being printed
  • while loop prints each design by removing it from the end of the list and storing it in current_design, before adding it to the list of completed models

Printing model: dodecahedron

Printing model: robot pendant

Printing model: phone case

The following models have been printed:

dodecahedron

robot pendant

phone case

  • can reorganize this code by writing two functions, each of which does one specific job
  • the first function will handle printing the designs, and the second will summarize the prints that have been made

def print_models(unprinted_designs, completed_models):

….”””

…Simulate printing each design, until none are left.

….Move each design to completed_models after printing.

….”””

….while unprint_designs:

……..current_design = unprinted_designs.pop()

……..print(f”Printing model: {current_design}”)

……..completed_models.append(current_design)

def show_completed_models(completed_models):

….”””Show all the models that were printed.”””

….print(“\nThe following models have been printed:”)

….for completed_model in completed_models:

……..print(completed_model)

unprinted_designs = [‘phone case’, ‘robot pendant’, ‘dodecahedron’]

completed_models = []

print_models(unprinted_designs, completed_models)

show_completed_models(completed_models)

  • define function print_models() with two parameters: a list of designs that need to be printed and a list of completed models
  • print each design by emptying one list and filling up the other
  • define show_completed_models() with one parameter: a list of completed models
  • easier to follow program that has the same output as the previous version without functions
  • this program is easier to extend and maintain, can call print_models() again if we need to print more designs
  • can change the code if we realize the printing code needs to be modified
  • more efficient than updating code separately in several places
  • every function has one specific job, which is more beneficial than using one function to do both jobs

Preventing a function from modifying a list

  • can keep the original list intact by modifying a copy of it

function_name(list_name[:])

  • slice notation [:] makes a copy of the list to send to the function

print_models(unprinted_designs[:], completed_models)

  • this will keep the original list of unprinted designs intact while filling up the completed_models list
  • only use when needed, as working with the original list is more efficient

End of study session.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *