1 of 36

CSCI100: Fall 2021

Lecture 14:

  • Function Review
  • Mutability Review
  • Destructive Functions

2 of 36

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…

  • What it means for a function to be “destructive” or “non-destructive”
  • Why we would want a function to be destructive
  • How we use destructive functions vs non-destructive functions

3 of 36

Context Review

4 of 36

Functions: A quick review...

5 of 36

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

6 of 36

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

7 of 36

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

8 of 36

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)

9 of 36

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)

10 of 36

Function Scope

11 of 36

Mutability: A quick review...

12 of 36

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)

13 of 36

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

14 of 36

Destructive Functions

15 of 36

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!

16 of 36

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.

17 of 36

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!

18 of 36

Destructive Functions

We call them destructive because they “destroy” (or at least change) the input that’s given.

19 of 36

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

20 of 36

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

21 of 36

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.

22 of 36

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.

23 of 36

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!

24 of 36

Code Examples

25 of 36

Destructive vs Non-destructive Append to a List

Let’s look at two ways you could write a function that appends to a list...

26 of 36

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)

27 of 36

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)

28 of 36

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)

29 of 36

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.

30 of 36

Destructive Functions: Why and When

31 of 36

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!)

  • “Does my function need to return a value?”
  • “Can I trust that this function call will not modify the list I give it?”
  • “Are there any side-effects to my arguments when calling this function?”

32 of 36

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.

33 of 36

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)

34 of 36

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)

35 of 36

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

36 of 36

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