Lecture 8 — Lists Part 2 ========================== Overview -------- - List copying versus list aliasing - Aliasing: creating new name for lists - Assignment - Function that change the lists that are input - Copying: creating new lists - Concatentation, replication and slicing - For loops to go through lists - Strings and lists - Lists of lists 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 - If we want to copy a list we should use a special set of methods. We'll see those next. Lists as Function Arguments --------------------------- - 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 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. What Operations Change a List? What Operations Create New Lists? ---------------------------------------------------------------- - Unlike strings, most operations on a list change the contents of the list rather than creating a new list. - Operations that change lists include - sort, insert, append, pop, remove - Operations that create new lists - ``+``, ``*``, slicing, ``list()`` Part 1 Exercises ---------------- #. 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 #. 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. #. Write a slicing command to extract values from a list ``L0`` indexed by 1, 4, 7, 10, etc. Loops: Writing Our Operations on Lists -------------------------------------- - Despite the presence of a number of list methods, we need to be able to invent new operations that we can apply to lists. - This requires the notion of a “loop” where we apply a computation to each item in a list. - In the next lecture, we will discuss a more general notion of a loop. - As an example, here is code to compute the average CO\ :math:`_2` level, as it would appear when typing directly into the Python interpreter: :: animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot'] all_animals = "" index = 1 for value in animals: all_animals += "%d. %s\n" %(index, value.capitalize()) index += 1 print all_animals - We can understand what is happening by looking at this piece-by-piece: - The keyword ``for`` signals the start of a loop - ``value`` 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 Part 2 Exercise --------------- #. Try the following code. Let's discuss what we have learnt from this: :: L1 = ['cat', 'dog', 'hawk', 'tiger', 'parrot'] for value in L1: value = 'sheep' print L1 #. Write a program to find the number of values greater than the average for the co2_levels given in the following list. Also, print the median of the values that are greater than average. :: co2_levels = [ 320.03, 322.16, 328.07, 333.91, 341.47, \ 348.92, 357.29, 363.77, 371.51, 382.47, 392.95 ] Aside: Reminder on Function Return Values ----------------------------------------- - Some functions do NOT return a value: - Examples include functions we wrote at the start of the semester and the sort, insert and append functions of lists. - What happens when you try to assign or print the value of a function that does not return anything? :: >>> t = [1, 5, 3 ] >>> u = t.sort() >>> print t >>> print u - Moral of the story: if a function does not return a value, do not try to use its return value!! 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 "", line 1, in TypeError: 'str' object does not support item assignment Part 3 Exercise --------------- #. 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. 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. Final Example: Thank You Generation ----------------------------------- - Let us create a program to send fancy “Thank you” notes to all our relatives who have given us gifts recently. - We’ll start by making a list of lists: :: gift_list = [['Uncle John', 'skateboard'], \ ['Aunt Marie', 'Amazon gift card'], ['Mr. Idle', 'backpack']] - Now we’d like a function that takes a name and a gift and prints a fancy output that might look something like: :: Dear Uncle John, Thank very much for your gift of a skateboard. I will enjoy using it very much. I hope you are doing well. Sincerely, John Cleese This function should have as arguments, the name of the giver, the name of the gift, and the gift recipient (you), e.g. :: def print_thank_you( giver, gift, recipient ): - Once we write this function, we will write a for loop to print all of the thank yous. - The result will be posted on the course web site. Part 4 Exercises ---------------- #. Given a list :: >>> L = [ 'cat', 'dog', 'tiger' ] write a line of code to append the string ``'lion'`` #. Rewrite ``L`` so that it is a list of lists, with household pets in the 0th (sub)list, zoo animals in the first. #. How can you append an additional list of farm animals (e.g. ``'horse'``, ``'pig'`` and ``'cow'``) to ``L``. #. 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? ------------------------------------------ #. 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 #. 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 #. 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.