A list is a sequence

  • A list is a sequence of values (like a string)
  • The values are referred to as elements or items
  • The simplest way to create a list is using square brackets

In [1]:
[10, 20, 30, 40]
['dog', 'fish', 'bird']


Out[1]:
['dog', 'fish', 'bird']
  • The elements of a list don't have to be the same data type in Python (in other languages they do)
  • However, if you want a sequence with different data types there are better alternatives

In [2]:
['bob', 3.14, 42, ['sam', 55]]


Out[2]:
['bob', 3.14, 42, ['sam', 55]]
  • As you can see, lists can be nested
  • A list containing no elements is an empty list
  • An empty list is created using empty brackets []
  • You can assign a list to a variable

In [3]:
cheeses = ['Chedder', 'Pepper Jack', 'Queso Fresca']
grades = [99, 84, 91]
empty = []

Lists are mutable

  • Unlike strings, lists are mutable
  • This means we can change them

In [4]:
cheeses = ['Chedder', 'Pepper Jack', 'Queso Fresca']
cheeses[0] = 'Gouda'
print( cheeses )


['Gouda', 'Pepper Jack', 'Queso Fresca']
  • Note that like strings, indices start at 0
  • In fact, all the things you can use for indices and use indices for in strings, you can do in lists
  • This includes the in operator
  • One way to think of the relationship between indices and elements is that it is a mapping
  • Each index maps to one of the elements
  • Much like a word maps to a definition in a dictionary
  • TODO State diagram for some lists

Traversing a list

  • The most common way to traverse a list is with a for loop
  • It is the same approach we used with strings

In [5]:
for cheese in cheeses:
    print( cheese )


Gouda
Pepper Jack
Queso Fresca
  • However, if you need the index, you need to use the range and len functions
  • For example, you may want to update the values in the list as you traverse it

In [6]:
numbers = [1, 2, 3]

for i in range( len( numbers ) ):
    numbers[i] = numbers[i] * 2

print( numbers )


[2, 4, 6]
  • A for loop over an empty list never executes the body of the loop
  • If you have a nested list, the nested list still counts as a single element

In [7]:
nested_list = ['spam', 1, [ 'Brie', 'Roquefort' , 'Pol le Veq'], [1, 2, 3] ]
len( nested_list )


Out[7]:
4

List operations

  • The + operator concatenates lists
  • This means that it links them together into a single list

In [8]:
a = [ 1, 2, 3 ]
b = [ 4, 5, 6 ]
c = a + b
print( c )


[1, 2, 3, 4, 5, 6]
  • Similarly, the * operator repeats a list a specified number of times

In [9]:
d = [ 0 ] * 4
print( d )

e = [ 1, 2, 3 ] * 3
print( e )


[0, 0, 0, 0]
[1, 2, 3, 1, 2, 3, 1, 2, 3]

List slices

  • The slice operator also works on lists:

In [10]:
f = [ 'a', 'b', 'c', 'd', 'e', 'f' ]
print( f[1:3] )
print( f[:4] )
print( f[3:] )


['b', 'c']
['a', 'b', 'c', 'd']
['d', 'e', 'f']
  • Just as with strings, if you omit the first index the slice starts at the beginning
  • If you omit the second, the slice goes to the end
  • If you omit both, the slice is th entire list
  • Since lists are mutable, sometimes it is important to make a copy of a list before you change it
  • You can use a slice to update multiple elements

In [11]:
f[1:3] = [ 'x', 'y' ]
print( f )


['a', 'x', 'y', 'd', 'e', 'f']

List methods

  • append will add a new element to the end of a list

In [12]:
list1 = [ 'a', 'b', 'c' ]
list1.append( 'd' )
print( list1 )


['a', 'b', 'c', 'd']
  • extend takes a list as an argument and appends all of the elements

In [13]:
list2 = [ 'a', 'b', 'c' ]
list3 = [ 'd', 'e', ]
list2.extend( list3 )

print( list2 )
print( list3 )


['a', 'b', 'c', 'd', 'e']
['d', 'e']
  • Note that list3 is unmodified
  • sort arranges the elements of a list from low to high

In [14]:
list4 = [ 'd', 'c', 'e', 'a', 'b' ]
list4.sort()
print( list4 )


['a', 'b', 'c', 'd', 'e']
  • All of these list methods are void
  • This means that they modify the list argument and return None

Map, filter and reduce

  • To add up all the numbers in a list, use a loop

In [15]:
def add_all( a_list ):
    total = 0

    for value in a_list:
        total += value

    return total
  • As we have seen before, the += operator updates a variable
  • It is referred to as an augmented assignment statement
  • In the code, total is used in the loop as an accumulator
  • Python provides the sum function to sum the elements of a list

In [16]:
list_of_numbers = [ 1, 2, 3 ]
sum( list_of_numbers )


Out[16]:
6
  • A function that combines a sequence of elements into a single value is sometimes called a reduce function
  • Sometimes you want to build a new list from an existing list

In [17]:
def capitalize_all( a_list ):
    result = []
    for a_string in a_list:
        result.append( a_string.capitalize() )
    return result

words = [ 'life', 'the', 'universe', 'and', 'everything' ]
capitalized_words = capitalize_all( words )
print( capitalized_words )


['Life', 'The', 'Universe', 'And', 'Everything']
  • This is sometimes called a map function since it "maps" a function onto each of the elements in a sequence
  • Note that a map is more commonly used to refer to a specific data structure (e.g., a dictionary)
  • Another common operation is to filter elements from one list to another

In [18]:
def only_upper( a_list ):
    result = []
    for a_string in a_list:
        if( a_string.isupper() ):
            result.append( a_string )
    return result

words = [ 'LIFE', 'The', 'uNiverse', 'AND', 'everything' ]
some_words = only_upper( words )
print( some_words )


['LIFE', 'AND']

Deleting elements

  • The pop function can be used to delete an element from a list if you know the index

In [19]:
a_list = [ 'a', 'b', 'c' ]
value = a_list.pop( 1 )
print( a_list )
print( value )


['a', 'c']
b
  • Note that the function returns the deleted element
  • If you don’t specify an index, it operates on the last element
  • If you don’t need the deleted value, use the del operator

In [20]:
a_list = [ 'a', 'b', 'c' ]
del a_list[1]
print( a_list )


['a', 'c']
  • del can also be used with a slice index to remove more than one element

In [21]:
a_list = [ 'a', 'b', 'c', 'd', 'e', 'f' ]
del a_list[1:5]
print( a_list )


['a', 'f']
  • If you know the element you want to remove, but not the index, use the remove operator
  • Note that it only removes the first matching element if there are duplicates

In [22]:
a_list = [ 'a', 'b', 'c', 'a', 'b', 'c' ]
a_list.remove( 'b' )
print( a_list )


['a', 'c', 'a', 'b', 'c']

Lists and strings

  • A list of characters is not the same as a string
  • To convert a string to a list, use the list operator

In [23]:
a_string = 'spam'
a_list = list( a_string )
print( a_list )


['s', 'p', 'a', 'm']
  • Since it is an operator, don’t use list as a variable name
  • Don’t use l either since it looks like a 1
  • Use a_list or something more descriptive detailing a value in your algorithm
  • If you want to split a string into words and not letters, use split

In [24]:
a_string = 'pining for the fjords'
a_list = a_string.split()
print( a_list )


['pining', 'for', 'the', 'fjords']
  • By default, the delimiter is a space
  • You can change that by passing it as an argument

In [25]:
a_string = 'spam-spam-spam'
delimiter = '-'
a_string.split( delimiter )


Out[25]:
['spam', 'spam', 'spam']
  • join is the inverse of split
  • Since it is a string method, you have to invoke it on a string
  • In this case, invoke it on the delimiter

In [26]:
a_list = [ 'pining', 'for', 'the', 'fjords' ]
delimiter = ' '
delimiter.join( a_list )


Out[26]:
'pining for the fjords'

Objects and values

  • To check whether two variables refer to the same object, use the is operator

In [27]:
a = 'banana'
b = 'banana'
a is b


Out[27]:
True
  • Note that this is different from the values of the two objects being equivalent

In [28]:
a = [ 1, 2, 3 ]
b = [ 1, 2, 3 ]
a is b


Out[28]:
False
  • In the first example, Python only create one string object
  • In the second, Python created two lists that are equivalent, but not identical
  • Why the difference?

Aliasing

  • A variable doesn’t actually hold an object, it has a reference to it
  • When two variables refer to the same object, it is called aliasing
  • If an object is mutable, changes made to one affect the other

In [29]:
a = [ 1, 2, 3 ]
b = a
b[0] = 17
print( a )


[17, 2, 3]
  • Be careful because this can cause problems if you aren’t paying attention
  • For immutable objects (like strings), it isn’t a problem

List arguments

  • When you pass a list as an argument to a function, it gets a reference to the list
  • What does this mean?
  • If the list modifies the list, it is the same list as in the calling function

In [30]:
def delete_head( a_list )
    del a_list[0]

letters = [ 'a', 'b', 'c' ]
delete_head( letters )
print( letters )


  File "<ipython-input-30-f8f590617787>", line 1
    def delete_head( a_list )
                             ^
SyntaxError: invalid syntax
  • See the stack diagram on pg. 97 of the textbook
  • Make sure you know if the function you are using modifies the list argument or returns a new list

In [31]:
list1 = [ 1, 2 ]
list2 = list1.append( 3 )
print( list1 )
print( list2 )


[1, 2, 3]
None

Debugging

  • Be careful of common pitfalls when using lists and other mutable objects:
    1. Most list methods modify the argument and return None. This is the opposite of string methods.
    2. Pick an idiom (or way of doing things) and stick with it
    3. Make copies to avoid aliasing and accidental modifications

Exercises

  • Write a function called cumulative_sum that takes a list of numbers and returns the cumulative sum; that is, a new list where the $i$-th element is the sum of the furst $i$ elements from the list.

In [32]:
def cumulative_sum( a_list ):
    # INSERT YOUR CODE HERE
    return []

numbers = [ 1, 2, 3 ]
result = cumulative_sum( numbers )
# The answer should be:
# [ 1, 3, 6 ]
  • Write a function called chop that takes a list and modifies it by removing the first and last element. The function should return None.

In [33]:
def chop( a_list ):
    # INSERT YOUR CODE HERE
    print( 'Remove this line' ) # Jupyter needs a statement to compile

numbers = [ 1, 2, 3, 4 ]
chop( numbers )
print( numbers )
# Should print: [ 2, 3 ]


Remove this line
[1, 2, 3, 4]
  • Two words are anagrams if you can rearrange the letters from one to spell the other. Write a function called is_anagram that takes two strings and returns True if they are anagrams.