Lecture 11 — Lists Part 2

Overview

  • Lists of lists
  • List copying versus list aliasing
  • Aliasing: creating new name for lists
    • Assignment
    • Function that change the lists that are input
  • For loops to go through lists
  • Copying: creating new lists
    • Concatentation, replication and slicing
  • Strings and lists

Lists of Lists

  • Lists can contain any mixture of values, including other lists.

  • For example, in

    >>> L = [ 'Alice', 3.75, ['MATH', 'CSCI', 'PSYC' ], 'PA' ]
    
    • L[0] is the name,
    • L[1] is the GPA
    • L[2] is a list of courses
    • L[2][0] is the 0th course, 'MATH'
    • L[3] is a home state abbreviation
  • We will write code to print the courses, to change the math course to a stats course, and to append a zipcode.

List Aliasing

  • The assignment operator does NOT create a new list. Rather it creates a new name for an existing list:

    >>> L1 = [ 'cat', 'dog', 'ant', 'tiger', 'parrot' ]
    >>> L2 = L1
    >>> L1
    ['cat', 'dog', 'ant', 'tiger', 'parrot']
    >>> L2
    ['cat', 'dog', 'ant', 'tiger', 'parrot']
    >>> L1[2] = 'hawk'
    >>> L1
    ['cat', 'dog', 'hawk', 'tiger', 'parrot']
    >>> L2
    ['cat', 'dog', 'hawk', 'tiger', 'parrot']
    
  • Surprised? This is called an alias. Compare it with:

    >>> S1 = 'this is a string'
    >>> S2 = S1
    >>> S1 = 'I changed the first string'
    >>> S1
    'I changed the first string'
    >>> S2
    'this is a string'
    
  • Python is designed to do this primarily for reasons of efficiency since

    • lists can be quite long and are frequently changed
    • strings tend to be shorter and tend to be fixed
  • This is true for different data types as well.

    • assignments create a copy for integers, floats, strings, booleans and tuples.
    • assignments create an alias for images, lists, sets and dictionaries (which we will see later).
  • If we want to copy a list we should use a special set of methods. We’ll see those next.

Aliasing and Function Parameters

  • When variables are passed to functions, a copy of their value is created for numbers, strings, booleans:

    def add_two(val1, val2):
        val1 += val2
        return val1
    
    val1 = 10
    val2 = 15
    print val1, val2
    print add_two(val1,val2)
    print val1, val2
    
  • When lists are passed to functions, the parameter becomes an alias for the argument in the function call.

    • Formally in computer science, this is known as pass by reference.
  • Here is an example of a function that returns a list containing the two smallest values in its input list:

    def smallest_two(mylist):
         mylist.sort()
         newlist = []
         if len(mylist) > 0:
             newlist.append(mylist[0])
             if len(mylist) > 1:
                 newlist.append(mylist[1])
         return newlist
    
    values = [35, 34, 20, 40, 60, 30]
    
    print "Before function:", values
    print "Result of function:", smallest_two(values)
    print "After function:", values
    
  • In class we will discuss what happened

For Loops: Writing Operations on List Items

  • A common operation in programs is to go through every element in a list using a loop. While loops can always be used for this, but a for loop simplifies this operation significantly.

  • Let’s first go through all the animals in a list, and print them out.

    animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot']
    all_animals = ""
    index = 1
    for animal in animals:
        animal = animal.capitalize()
        all_animals += "%d. %s, " %(index, animal)
        index += 1
    
    print all_animals.strip()
    
  • We can understand what is happening by looking at this piece-by-piece:

    • The keyword for signals the start of a loop
    • animal is a loop variable that takes on the value of each item in the list in the list (as indicated by the keyword in) in succession
    • The : signals the start of a block of code that is the “body of the loop”
    • The body of the loop here is just a single, indented line of code, but in other cases it can be many more.
    • The end of the loop body is indicated by the blank line
    • The most important of all, each value in the list is copied to the variable animal at each step of the for loop.

Part 1 Exercise

  1. Change the above code to capitalize all the animal names in the list.

  2. Write a function to capitalize all the names in an input list.

  3. Write a program to find the number of values greater than the average for the co2_levels given in the following list.

    co2_levels = [ 320.03, 322.16, 328.07, 333.91, 341.47, \
      348.92, 357.29, 363.77, 371.51, 382.47, 392.95 ]
    
  4. Suppose you have the following pieces of code. Discuss what is different between the two.

    mylist = [2,8,11]
    
    for item in mylist:
        item *= 2
    
    mylist2 = [ [2], [8], [11] ]
    
    for item in mylist:
        item[0] *= 2
    

Using range to create a list of numbers

  • When you need to index a list, you need to generate numbers to serve as indices.

  • While solution:

    animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot']
    i = 0
    while i < len(animals):
        print animals[i]
        i += 1
    
  • With a while loop, you have to be careful not to cause an infinite loop. You can also do the same with a special list of just consecutive numbers.

    ::
    >>> range(len(animals))
    [0, 1, 2, 3, 4]
    
  • Now, we can use the for loop as we have seen to achieve the same result.

    animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot']
    for i in range(len(animals))
        print animals[i]
  • Note: no need to increment a counter anymore. We will see range in more detail next class.

What Operations Change a List? What Operations Create New Lists?

  • Given assignment does not create a copy of a list, what are some methods that allow us to create copies of a list?
  • Operations that change lists include
    • sort, insert, append, pop, remove
  • Operations that create new lists
    • Slicing, concatenation (+), replication (‘’*) and ``list()
  • We will see these shortly.

Using Indices to “Slice” a List and Create a New List

  • Recall

    >>> co2_levels = [ 320.03, 322.16, 328.07, 333.91, 341.47,
          348.92, 357.29, 363.77, 371.51, 382.47, 392.95 ]
    
  • Now suppose we just want the values at indices 2, 3 and 4 of this in a new list:

    >>> three_values = co2_levels[2:5]
    >>> three_values
    [328.07, 333.91, 341.47]
    >>> co2_levels
    [ 320.03, 322.16, 328.07, 333.91, 341.47, 348.92, 357.29, 363.77,
       371.51, 382.47, 392.95 ]
    
  • We give the first index and one more than the last index we want

  • If we leave off the first index, 0 is assumed, and if we leave off the last index, the length of the list is assumed.

  • Negative indices are allowed — they are just converted to their associated positive values. Some examples:

    >>> L1
    ['cat', 'dog', 'hawk', 'tiger', 'parrot']
    >>> L1[1:-1]
    ['dog', 'hawk', 'tiger']
    >>> L1[1:-2]
    ['dog', 'hawk']
    >>> L1[1:-4]
    []
    >>> L1[1:0]
    []
    >>> L1[1:10]
    ['dog', 'hawk', 'tiger', 'parrot']
    

More on List Slicing

  • The most general form of slicing involves three values

    L[si:ei:inc]
    

    where

    • L is the list
    • si is the start index
    • ei is the end index
    • inc is the increment value

    Any of the three values is optional

  • We’ll work through some examples in class to

    • Use slicing to copy an entire list
    • Use negative indices for slicing
    • Extracting the even indexed values
  • Note: L[:] returns a copy of the whole list of L. This is the same using function list(L):

    >>> L2 = L1[:]
    >>> L2[1] = 'monkey'
    >>> L1
    ['cat', 'dog', 'hawk', 'tiger', 'parrot']
    >>> L2
    ['cat', 'monkey', 'hawk', 'tiger', 'parrot']
    >>> L3 = list(L1)
    >>> L3[1] = 'turtle'
    >>> L1
    ['cat', 'dog', 'hawk', 'tiger', 'parrot']
    >>> L2
    ['cat', 'monkey', 'hawk', 'tiger', 'parrot']
    >>> L3
    ['cat', 'turtle', 'hawk', 'tiger', 'parrot']
    

Concatentation and Replication

  • Concatenation:

    >>> v = [1,2,3]+[4,5]
    >>> v
    [1,2,3,4,5]
    
  • Replication:

    >>> [1]*3
    [1,1,1]
    
  • These are very similar to the analogous operations with strings.

Part 2 Exercises

  1. What is the output of the following?

    x = [6,5,4,3,2,1] + [7]*2
    y = x
    x[1] = y[2]
    y[2] = x[3]
    x[0] = x[1]
    print x
    
    y.sort()
    print x
    print y
    
  2. Write a command to extract values from a list L0 indexed by 0,1,4,7 and 10,11, and return a list containing only these values.

  3. Write a slicing command to extract values from a list L0 indexed by 1, 4, 7, 10, etc.

Converting Strings to Lists

  • Version 1: use the function list to create a list of the characters in the string:

    >>> s = "Hello world"
    >>> t = list(s)
    >>> print t
    ['H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
    
  • Use the string split function, which breaks a string up into a list of strings based on the character provided as the argument.

    • The default is ' ':
    • Other common splitting characters are ',', '|' and '\t'
  • We will play with the s = "Hello world" example in class.

Converting Lists to Strings

  • What happens when we type the following?

    >>> s = "Hello world"
    >>> t = list(s)
    >>> s1 = str(t)
    

    This is unlikely to achieve what we want, which is usually the concatention of all of the strings in the list (assumming they are strings).

  • We will solve this problem using a for loop in the next exercise.

Indexing and Slicing Strings

  • We can index strings:

    >>> s = "Hello, world!"
    >>> print s[5]
    ,
    >>> print s[-1]
    !
    
  • We can apply all of the slicing operations to strings to create new strings:

    >>> s = "Hello, world!"
    >>> s[:len(s):2]
    'Hlo ol!'
    
  • Unlike lists, however, we can not use indexing to replace individual characters in strings:

    >>> s[4] = 'c'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'str' object does not support item assignment
    

Part 3 Exercise

  1. Suppose you have the string

    >>> s = "cat |  dog  | mouse"
    

    and you’d like to have the list of strings

    >>> L = [ "cat", "dog", "mouse"]
    

    Splitting the list alone does not solve the problem. Instead, you need to use a combination of splitting, and a loop that strips off the extra space characters from each string and appends to the final result. Write this code. It should be at most 4-5 lines of Python.

  2. Given a list

    >>> L = [ 'cat', 'dog', 'tiger' ]
    

    write a line of code to append the string 'lion'

  3. Rewrite L so that it is a list of lists, with household pets in the 0th (sub)list, zoo animals in the first.

  4. How can you append an additional list of farm animals (e.g. 'horse', 'pig' and 'cow') to L.

  5. Write code to remove 'tiger' from the sublist of zoo animals.

Summary

  • We can write our own functionality to work with lists using for loops.
  • Concatentation, replication and slicing create new lists.
  • Most other list functions that modify a list do so without creating a new list: insert, sort, append, pop, etc.
  • Assignment of lists and passing a list to a function do NOT create new lists, just aliases of existing lists.
  • Strings may be indexed and sliced, but indexing may not be used to change a string.
  • Conversion of a string to a list is accomplished using either list or split
  • Lists can be converted to strings as well, but it takes more work — it is not a natural operation.
  • Lists can store other lists.

Review Exercises: What Does Python Output?

  1. Without typing into the Python interpreter, find the outputs from the following operations:

    >>> x = ['a','b','c','d', 'e']
    >>> print x
    
    >>> for item in x:
    ...    print "*%s*" %item,
    ...
    
    >>> print x[3]
    
    >>> x[3] = 3
    >>> x
    
    >>> len(x)
    
    >>> x[2]=x[1]
    >>> x
    
    >>> x[5]
    
    >>> y = x[1:4]
    
    >>> y
    
    >>> x
    
  2. What about these operations?

    >>> y = [1, 2, 3]
    
    >>> y.append('cat')
    
    >>> y
    
    >>> y.pop()
    
    >>> y
    
    
    >>> y.remove(2)
    >>> y
    
    >>> y.remove('cat')
    
    >>> z = ['cat','dog']
    >>> z.insert(1,'pig')
    >>> z.insert(0,'ant')
    >>> z
    
    >>> z.sort()
    >>> z
    
    >>> z1 = z[1:3]
    >>> z1
    
    >>> z
    
  3. Write a function that returns a list containing the smallest and largest values in the list that is passed to it as an argument without changing the list? Can you think of ways to do this with sorting and without? Hint: use a for loop.