CSCI100: Fall 2021
Lecture 14:
Learning Objective
This lecture builds heavily on the functions lectures and the list/mutability lectures.
By the end of today’s lecture, you will understand…
Context Review
Functions: A quick review...
Functions
A function is a named block of code that can take some input, performs some routine, and can return some output.
def add_nums(num1, num2): result = num1 + num2 return result |
Functions
A function is a named block of code that can take some input, performs some routine, and can return some output.
def add_nums(num1, num2): result = num1 + num2 return result |
Parameters
Functions
The list of parameters in the function definition are the inputs that the function takes. They are the variable names are used inside of body of the function to access the input values.
def add_nums(num1, num2): result = num1 + num2 return result |
Parameters
Functions
We call (or execute) the function by using the name of the function, and giving a list of arguments in parentheses.
def add_nums(num1, num2): result = num1 + num2 return result total = add_nums(4, 5) |
Functions
We call (or execute) the function by using the name of the function, and giving a list of arguments in parentheses.
These argument values are assigned to the variable names listed in the function definition for the duration of the function execution.
def add_nums(num1, num2): result = num1 + num2 return result total = add_nums(4, 5) |
Function Scope
Mutability: A quick review...
Mutable vs Immutable
Mutable data types are types whose value can be modified or altered. In this class, these are only lists and dictionaries so far.
items = [4, 9] items.append(7) |
Mutable vs Immutable
Mutable data types are types whose value can be modified or altered. In this class, these are only lists and dictionaries so far.
All other types we have learned about (strings, ints, booleans, and floats) are all immutable, because they cannot be modified, only overwritten.
items = [4, 9] items.append(7) |
X 9
score = 2 score = 9 |
Destructive Functions
Destructive Functions
When you were only using immutable values as parameters, you could only return a value (or use the value to print/write to a file).
However, when you write a function that takes in a mutable value (list or dictionary) as an argument, you have the additional option of mutating the given value!
Destructive vs Non-destructive Functions
A destructive function is a function that mutates any of its mutable parameters. Ex: Updating a value in a given dictionary, removing an item from a given list, etc.
A non-destructive function is a function that doesn’t mutate any of the parameters.In other words, any function that isn’t destructive.
Destructive vs Non-destructive Functions
A destructive function is a function that mutates any of its mutable parameters. Ex: Updating a value in a given dictionary, removing an item from a given list, etc.
A non-destructive function is a function that doesn’t mutate any of the parameters.In other words, any function that isn’t destructive.
Notice, a function that has no parameters or only immutable parameters is non-destructive by definition, because it has nothing it can mutate!
Destructive Functions
We call them destructive because they “destroy” (or at least change) the input that’s given.
Destructive vs Non-Destructive
Imagine I have a Todo List with various tasks for the day…
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice trumpet
Destructive vs Non-Destructive
Imagine I have a Todo List with various tasks for the day…and I ask you to mark off the first item as completed.
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice trumpet
Destructive vs Non-Destructive
Imagine I have a Todo List with various tasks for the day…and I ask you to mark off the first item as completed.
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice trumpet
Destructive Approach
Take a marker and cross out the first item on the list.
Destructive vs Non-Destructive
Imagine I have a Todo List with various tasks for the day…and I ask you to mark off the first item as completed.
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice violin
Destructive Approach
Take a marker and cross out the first item on the list.
This is destructive because it mutates the original task list that I gave you. You wrote on the list that I gave you.
Destructive vs Non-Destructive
Imagine I have a Todo List with various tasks for the day…and I ask you to mark off the first item as completed.
Non-destructive Approach
Make a scan copy of the list you gave me, cross out the first item (leaving the original you gave me intact), and return to you the updated copy.
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice violin
input (argument)
Task List
☐Brush teeth
☐Clean room
☐Make bed
☐Finish homework
☐Practice violin
copy (return)
This is non-destructive because it leaves the mutable input intact!
Code Examples
Destructive vs Non-destructive Append to a List
Let’s look at two ways you could write a function that appends to a list...
Destructive Append
numbers = [4, 7, 2] def destructive_append(items, new_item): # Mutate the list to add another item items.append(new_item) destructive_append(numbers, 9) print(numbers) |
Non-destructive Append
numbers = [4, 7, 2] def nondestructive_append(items, new_item): # Make a copy, modify it, and return the copy new_list = [] for item in items: new_list.append(item) new_list.append(new_item) return new_list new_numbers = nondestructive_append(numbers, 9) print("Original:", numbers) print("New List:", new_numbers) |
Non-destructive Append...prettier
An even cleaner way to write the non-destructive version is to use slicing to make the copy...
numbers = [4, 7, 2] def nondestructive_append(items, new_item): new_list = items[:] new_list.append(new_item) return new_list new_numbers = nondestructive_append(numbers, 9) print("Original:", numbers) print("New List:", new_numbers) |
Using Destructive vs Non-destructive Functions
Notice that destructive functions don’t need to have a return value, because they simply modify what was given to them.
Whereas non-destructive functions almost always have a return value, because they have to make a copy of what was given to them and return the new value, and that value usually needs to be assigned to a new variable.
Destructive Functions: Why and When
Why Destructive/Non-destructive Matters
This is important to always keep in mind to set mental expectations for the programmer using your function (which may be you!)
When to write/use a destructive function?
Write/use a destructive function when you want to update some mutable value and whoever calls the function doesn’t need the previous value anymore.
Write/use a non-destructive function if whoever calls your function may need the previous value.
When to write/use a destructive function?
Should these filter functions be destructive or non-destructive?
image = image_io.read_file('obama.png') result1 = flip_filter(image) result2 = grayscale_filter(image) result3 = blur_filter(image) |
When to write/use a destructive function?
Should these filter functions be destructive or non-destructive?
Answer: They should be non-destructive, because we reuse the original image, and therefore need it to stay intact.
If they were destructive, then results would be flipped and grayscale, and result 3 would be flipped, grayscale, and blurred.
image = image_io.read_file('obama.png') result1 = flip_filter(image) result2 = grayscale_filter(image) result3 = blur_filter(image) |
Parting Quiz Question
Question: What does this function do, and is it destructive or non-destructive?
def mystery(a): b = a[:2] + a[:3] return b |
Parting Quiz Question
Question: What does this function do, and is it destructive or non-destructive?
Answer: This function returns a new list made up of the first 2 elements in a, followed by the first 3 elements in a. It is non-destructive because slicing creates a new list, and we add the 2 new sliced lists together.
The original input list (a) is left untouched.
def mystery(a): b = a[:2] + a[:3] return b |