Lecture 10 — Lists Part 2¶
Topics¶
- List aliasing, lists and functions
- For loops to operate on lists
- Slicing to create copies of lists and to create sublists
- Converting back and forth between strings and lists
List Aliasing¶
Consider the following example Python code:
>>> L1 = [ 'RPI', 'WPI', 'MIT' ] >>> L2 = L1 >>> L3 = [ 'RPI', 'WPI', 'MIT' ] >>> L2.append( 'RIT' ) >>> L2[1] = 'CalTech' >>> L1 ['RPI', 'CalTech', 'MIT', 'RIT'] >>> L2 ['RPI', 'CalTech', 'MIT', 'RIT'] >>> L3 ['RPI', 'WPI', 'MIT']
Surprised? This is caused by the creation of what we call an alias in computer science:
- L1 and L2 reference the same list - they are aliases of each other and the underlying list - so changes made using either name change the underlying list
- L3 references a different list that just happens to have the same string values in the same order: there would have been no confusion if the names had been different.
- We’ll use our memory model for lists to understand what is happening here.
Python uses aliases for reasons of efficiency: lists can be quite long and are frequently changed, so copying of entire lists is expensive
This is true for other container data types as well.
- Assignments create an alias for images, lists, tuples, strings and, as we
will see later, sets and dictionaries
- Strings and tuples are special because they can’t be changed without changing them entirely.
- Assignments create an alias for images, lists, tuples, strings and, as we
will see later, sets and dictionaries
If we truly want to copy a list rather than alias it, we should use a special set of methods discussed below.
Aliasing and Function Parameters¶
When variables are passed to functions, a copy of their value is created for numbers and 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
What Operations Change a List? What Operations Create New Lists?¶
- Operations that change lists include
sort,insert,append,pop,remove
- Operations that create new lists
- Slicing (discussed below), concatenation (
+), replication (*) andlist()
- Slicing (discussed below), concatenation (
Part 1 Exercises¶
What is the output of the following code?
L1 = [ 'Kentucky', 'Vermont', 'New York' ] L2 = L1 L1.append( 'Ohio' ) L1 = [ 'Kentucky', 'Ohio' ] L3 = L1 L1.pop() print L1 print L2 print L3
Write a function to capitalize all the names in a list passed as an argument by returning a new list that contains the capitalized values. Does the original list change?
Show the output of the following two
forloops and explain the difference (in terms of list aliasing)mylist = [2,8,11] for item in mylist: item *= 2 print mylist mylist2 = [ [2], [8], [11] ] for item in mylist2: item[0] *= 2 print mylist2
Part 2: For Loops and Operations on List Items¶
A common operation in programs is to go through every element in a list using a loop. While loops can be used for this, but a Python
for loopsimplifies 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 = "" for animal in animals: all_animals.append( animal.capitalize() ) print all_animals
We can understand what is happening by looking at this piece-by-piece:
- The keyword
forsignals the start of a loop animalis a loop variable that takes on the value of each item in the list (as indicated by the keywordin) in succession- This is called iterating over the values/elements of the lsit
- The
:signals the start of a block of code that is the “body of the loop”, executed once in succession for each value thatanimalis assigned - The body of the loop here is just a single, indented line of code, but in other cases there may be many more lines of code.
- The end of the loop body is indicated by returning to the same
level of the indentation as the
for ...line that started the loop.
- The keyword
A word of caution: Do not iterate over list elements with a
forloop to change the elements, unless the elements are containers themselves.
Using range¶
So, based on what we know so far, if we wanted to capitalize all of the strings in a list, without creating a new list we would need to use a
whileloop with a separate index variable - often using the variable nameiorj:animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot'] i = 0 while i < len(animals): animals[i] = animals[i].capitalize() i += 1 print animals
We can simplify this a bit using a
rangeto create a list of indices:>>> range(len(animals)) [0, 1, 2, 3, 4]
and then using a
forloop that iterates over the values in this list:animals = ['cat', 'monkey', 'hawk', 'tiger', 'parrot'] for i in range(len(animals)) animals[i] = animals[i].capitalize() print animals
There is no need to write code to compare our index / counter directly against the bound and no need to write code to increment its value.
This use of
rangeto generate an index list is common- When we want to change the integer, float or string values of a list.
- When we want to work with multiple lists at once.
Part 2 Exercises¶
Recall our 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 ]
For the purpose of this exercise only, please pretend the Python
sumfunction does not exist, and then write a short section of Python code to first compute and then print the sum of the values in theco2_levelslist. You do not need to use indexing.Write a Python function that is passed the
co2_levelsvariable as an argument, and returns the number of values that are greater than the average value. For this you may use Python’ssumandlenfunctions as part of your solution. Again, you do not need to use indexing.Suppose we discovered that the measurement of CO2 values was uniformly too low by a small fraction
p. Write a function that increases each value inco2_levelsby the fractionp. For this problem you need to use arangeand indexing.
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
Lis the listsiis the start indexeiis the end indexincis 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 increments and generate a reversed list
- Extracting the even indexed values
Note:
L[:]returns a copy of the whole list ofL. This is the same using functionlist(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 3 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
L0indexed by 0,1,4,7 and 10,11, and return a list containing only these values. No slicing is needed.Write a slicing command to extract values from a list
L0indexed by 1, 4, 7, 10, etc.
Converting Strings to Lists¶
Version 1: use the function
listto 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']
Version 2: use the string
splitfunction, 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'
- The default is
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 will not concatenate all the strings in the list (assumming they are strings).
We can write a
forloop to do this, but Python provides something simpler that works:>>> L1 = [ 'No', 'one', 'expects', 'the', 'Spanish', 'Inquisition' ] >>> print ''.join(L1) NooneexpectstheSpanishInquisition >>> print ' '.join(L1) No one expects the Spanish Inquisition
Can you infer from this the role of the string that the
joinfunciton is applied to?
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 4 Exercises¶
Given a list
>>> L = [ 'cat', 'dog', 'tiger' ]
write a line of code to append the string
'lion'Rewrite
Lso 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') toL.Write code to remove
'tiger'from the sublist of zoo animals.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.
Summary¶
- Assignment of lists and passing of lists as parameters creates aliases of lists rather than copies.
- We use
forloops to iterate through a list to work on each enty in the list. - We need to combine
forloops with indices generated by arangein order to change the contents of a list of integers, floats or strings. These indices are also used to work with multiple lists at once. - 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.
- 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
listorsplit; conversion of a list of strings to a string usesjoin.
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 several ways to do this?
- Using
minandmax - Using sorting (but remember, you can’t change the original list)
- Using a
forthat searches for the smallest and largest values.
- Using