Week 6: Lists; More Functions

Week 6 Goals

  • Learn to represent data using lists

  • Learn about list indexing other operations

  • Learn how lists are used by functions

  • Learn how characters are represented using ASCII encoding

Get Week 6 In-class Code

To copy over the week 6 in-class example programs, do the following (If you have trouble with either of these steps, ask a Ninja or your professor for help):

  1. Create a w06-lists subdirectory in your cs21/inclass directory, and cd into it:

    $ cd ~/cs21/inclass
    $ mkdir w06-lists
    $ cd w06-lists
    $ pwd
    /home/yourusername/cs21/inclass/w06-lists
  2. Copy over the week 6 files into your w06-lists subdirectory (check that they copied successfully copied by running ls:

    $ cp ~admin21/public/w06-lists/*.py ./
    $ ls
    ascii.py biggest_circle.py largest.py lists.py list_functions.py

Week 6 Code

  • largest.py: more practice with functions

  • lists.py: practice with lists and list operations

  • list_functions.py: practice with functions that use lists

  • biggest_circle.py: functions that use lists and graphics

  • ascii.py: examples of using character encoding

Week 6 Concepts

Lists

Until now, we have seen that a variable can hold a single value, e.g. if we set the variable x equal to 5, then x only holds the value 5.

A list is a variable that can hold multiple values, which are stored in an ordered manner.

In the following code, we use the bracket notation to create a list called nums that holds the values 5, 6, 4, and 2, in that order:

nums = [5, 6, 4, 2]
print(nums) # prints [5, 6, 4, 2]

We can access individual elements of a list using a 0-based index:

nums = [5, 6, 4, 2]

print(nums[1]) # prints 6

nums[0] = 11
print(nums) # prints [11, 6, 4, 2]

We can also get the number of elements in a list using the len function:

nums = [11, 6, 4, 2]
print(len(nums)) # prints 4

Working with Lists

We can iterate over the elements of a list in the same manner that we did with strings: either by iterating one element/character at a time, or by using the indices within the valid range to get each element/character by its index.

The following two for-loops produce the exact same output:

dogs = ['snoopy', 'bluey', 'pluto']

for dog in dogs:
    print(dog + " is a good dog")

for i in range(len(dogs)):
    print(dogs[i] + " is a good dog")

We can add elements to the end of a list using the append method:

dogs = ['snoopy', 'bluey', 'pluto']

dogs.append('underdog')

print(dogs) # prints ['snoopy', 'bluey', 'pluto', 'underdog']

When are also able to concatenate two lists in order to create a larger list. Note that this does not change either of the two lists, but rather creates a new one:

weekdays = ['mon', 'tue', 'wed', 'thu', 'fri']
weekend = ['sat', 'sun']

week = weekdays + weekend

print(week) # prints ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']

Often we may want to start with an empty list, i.e. one containing no elements, and then add some number of elements to it. The following code asks the user how many elements to add, then appends elements as they are provided:

songs = []
n = int(input("How many favorite songs do you have? "))
for i in range(n):
    song = input("Tell me one of your favorite songs: ")
    songs.append(song)
print(songs) # prints all songs in the list

Passing Lists to Functions

Lists can be passed as arguments to function. The parameter of the function will refer to the entire list.

The following function calculates the sum of the values in the list that is passed as an argument:

def sum(lst):
    total = 0
    for i in range(len(lst)):
        total = total + lst[i]
    return total

An important difference between lists and other variables is that when a list is passed to a function, the function can modify the list, whereas it cannot modify arguments such as ints, floats, and strings.

For instance, consider the following function, which is attempting to change the value that is sent to it:

def change(x):
    x = 0

Now let’s say we call this function as follows:

a = 5
change(a)
print(a) # prints 5

The code still prints 5, because the change function has only changed the value of the parameter x, not the value of the argument a.

However, lists behave differently when passed to functions:

def change_list(lst):
    lst[0] = 0
    lst.append(17)

Now when we call this function, the list is changed:

nums = [5, 7, 9]
change_list(nums)
print(nums) # prints [0, 7, 9, 17]

This happens because the parameter lst and the argument nums are aliases for the same list. This means that the list has two names: lst and nums.

Let’s take a look at the stack diagram to see why this happens!

First, here is what the stack diagram looks like for the code that calls change_list: the nums variable refers to the list containing [5, 7, 9]:

Stack diagram for function with list as local variable

When change_list is called, we pass nums as the argument. This means that the value in the box labeled nums is copied to the box labeled lst, i.e. the parameter of the change_list function. What’s in the box labeled nums is the arrow pointing to the list, so now lst has an arrow pointing to that list, too.

Stack diagram for function with two variables referring to same list

Now when change_list runs the first line of code — lst[0] = 0 — we change what is in the box for element #0 of the list. So the arrow now points to 0 instead of 5.

Stack diagram for function that has changed value in list

Next, when change_list runs the second line of code — lst.append(17) — a new box is added to the list, and it points to 17.

Stack diagram for function that has appended value to list

Finally, when change_list returns and we go back to main, the variable nums is still pointing to the same list, which has been updated by change_list. This is why nums[0] is now 0 instead of 5.

Stack diagram for function that has had list modified after calling another function

Character Encoding

Any piece of data that we use in our programs has two attributes: its value and its type.

For instance, if we have x = 8, then the variable x has the value 8 and the type "int".

Even the literal 1.7 has a value 1.7 and type "float".

The data’s value is always stored as a number, and the type determines which operations are legal and how they function.

But what about strings? For instance, how is "bingo" stored as a number?

Recall that a string is a sequence of individual characters, and though a string is not the same as a list, we can think of it as storing the individual characters "b", "i", "n", "g", and "o".

Then how are those characters' values stored as numbers?

To represent the individual characters, Python encodes them using an agreed-upon convention or standard that assigns numbers to individual characters, including letters of the alphabet but also punctuation, whitespace, and numerical characters such as "7".

A popular standard for encoding characters is ASCII, which was developed in the 1960s and is still commonly used today.

In Python, we can find the encoding of an individual character by using the ord(ch) function, which returns the ASCII encoding of the character ch:

letter = "k"
print(ord(letter)) # prints 107, which is the ASCII encoding of "k"

We can also go the other direction using the function chr(num), which returns the character that has num as its encoding:

n = 63
print(chr(n)) # prints "?", which has 63 as its ASCII encoding