Observera att denna sida är under utveckling!

Lesson 6: Lists and tuples

Subject: A more systematic review of lists and tuples
New concepts: Lists and tuples
Work procedure: You are encouraged to discuss the work with others but should write your own code.

Try to solve the exercises before you look at the answers. Ask a teaching assistant if you don't understand the answers.

Estimated working time: 6 hours.
Examination: It is mandatory to present your solution to a teacher or assistant teacher according the time at the course home page.

Lists

We've already used lists in previous lessons but now we'll go through the concept and usage more thoroughly.

The list is one of the most important and useful tools in Python. The list concept also includes what in many other (programming) languages are called arrays, but lists are more general than arrays usually are.

A list consists of a number of elements of arbitrary types.

Examples:

Construction of lists

A list can be created by literally listing all the objects within square brackets, as done in the examples above.

The elements are indexed starting at 0. An element can be accessed by putting its index in the square brackets.

The operator = and the methods append and insert can be used to change the list.

ExamplePrintoutComment
fib = [1, 1, 2, 3, 5, 8] print(fib) [1, 1, 2, 3, 5, 8]
print(fib[3]) print(fib[2] + fib[5]) 3 10 Accessing single elements using []. First index is always 0.
fib.append(12) print(fib) [1, 1, 2, 3, 5, 8, 12] The method append adds the new element at the end of the list; it appends the element.
fib[6] = 13 print(fib) [1, 1, 2, 3, 5, 8, 13] A single element can be changed using the assignment operator.
fib.insert(0, 0) print(fib) [0, 1, 1, 2, 3, 5, 8, 13] insert(index, value) inserts a new value at the given index.

We can use negative index to reference objects at the end of the list. Index -1 references the last element, -2 the next to last, etc.

ExamplePrintout
fib.append(fib[-1] + fib[-2]) print(fib) [0, 1, 1, 2, 3, 5, 8, 13, 21]
for i in range(3): fib.append(fib[-1] + fib[-2]) print(fib) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Working with lists

There are several standard functions for lists, such as len for the number of elements, max and min for the largest and smallest value(s), sum for the sum of all the elements, and sorted which returns a new list that is sorted. All of these except the first one require the elements of the list to be "comparable", that is, the elements can all be compared to one another. For example, an int and a string is not comparable, so a list containing both ints and strings could not make use of those methods.

ExamplePrintoutComment
m = [1, 3, 0.5, 4, 3.5, 8] print(len(m)) 6
print(max(m), min(m), sum(m)) 8 0.5 20.0
print(sorted(m)) print(m) [0.5, 1, 3, 3.5, 4, 8] [1, 3, 0.5, 4, 3.5, 8]
The original list is unaltered.
print(sorted(['Ärlig', 'Eva', 'Åke'])) ['Eva', 'Ärlig', 'Åke'] Note: 'Å' and 'Ä' have not been sorted correctly. In the Swedish alphabet, 'Ä' comes after 'Å' ( ...XYZÅÄÖ).
sorted(['x', 5]) Traceback (most recent call last): File "<stdin", line 1, in <module> TypeError: '<' not supported between
instances of 'int' and 'str'
The datatypes int and str are not comparable.

Exercises

  1. Write a function mean(m) that calculates and returns the mean value of the numbers in the list m! What happens if the list contains strings? Answer
    def mean(m):
        return sum(m)/len(m)
    
  2. Write a function median(m) that calculates and returns the median value of the numbers in the list m!

    The median is the value that is placed in the middle when the list is sorted. If the number of elements is even, we define the median as the mean value of the two elements in the middle.

    For example, for a list with eight elements the median would mean value of the third and forth element.

    Answer
    def median(m):
        l = sorted(m)
        mid = len(l)//2
        if len(l)%2 == 1:
            return l[len(l)//2]
        else:
            return(l[mid-1] + l[mid])/2
    

The for statements on lists

The for loop is perfect for iterating over all the elements in a list.

Example: Sum all the negative numbers in a list.

m = [-1, 3, 5, -3, 3, -8]
sum = 0
for x in m:
    if x < 0:
        sum += x
print(sum)            # Prints out -12

Exercises

  1. Given a list with numbers, write code that constructs a new list without the negative numbers. Answer
    m = [-1, 3, 5, -3, 3, -8]
    n = []              # Create an empty list
    for x in m:
        if x >= 0:
            n.append(x)
    print(n)            # Prints out [3, 5, 3]
    
  2. Write a function between(a_list, low, high) that creates and returns a list of the elements in the given a_list that lays between low and high. Answer
    def between(a_list, low, high):
        result = []
        for x in a_list:
            if low <= x <= high:    # It's OK to write like this!
                result.append(x)
        return result
    
    l = [3, 1, 8, 19, 2, 5, 12]
    print(between(l, 3, 12))        # Prints out [3, 8, 5, 12]
    

Example: World of wandering turtles

Instead of the two turtles we had in the previous lesson, we will now have a list with several dizzy turtles. Using the methods random_turtle and move_random from the previous lesson:

# Create a set of dizzy turtles
turtles = []
for t in range(6):
    turtles.append(random_turtle())

for i in range(1, 200):
    for t in turtles:
          move_random(t)

You can find the entire program here. Feel free to download and test it!

Exercise

  1. bild1

    Create a list with 4 turtles and place them in the corners of a square. Point the first turtle towards the second, the second towards the third, the third towards the fourth, and the fourth towards the first. Like this:

    bild2

    Then, let the turtles walk one step (length 5) towards the turtle it is pointing at. Update the directions of the turtles so that they are pointing towards their target turtle's new position. After a while, it'll look like this:

    Stop the program when the turtles meet in the middle.

    Answer
    ts = [create_turtle(-200, 200, 270),
    create_turtle(-200, -200, 0),
    create_turtle(200, -200, 90),
    create_turtle(200, 200, 180)]
    
    while ts[0].distance(ts[1]) > 10:
        for t in ts:
        t.forward(5)
    
        for i in range(4):
            j = (i + 1) % 4    # index for "the next" turtle
            ts[i].setheading(ts[i].towards(ts[j]))
    

    A program with the help function create_turtle can be found here.

Partial lists — "slicing"

We can create partial lists using colons.

SliceMeaning
lst[m:n] The part of the list lst that has index from m to n - 1.
lst[:] A new list with the same elements as lst.
lst[m:] A list with all the elements at index m and up.
lst[:m] All elements from the beginning of the list up until the element with index m-1.

Example

Some examples using the list lst = [10, 11, 12, 13, 14, 15]

CodeValue
lst[2:5] [12, 13, 14]
lst[:2] [10, 11]
lst[2:2] []
lst[:] [10, 11, 12, 13, 14, 15]
lst[-3:] [13, 14, 15]
lst[-3:10] [13, 14, 15]
lst[-100:2][10, 11]

As is evident, it is quite alright to use index values in the colon expression that are actually outside of range for the list in question.

It is also possible to change the list using both the assignment operator and the slicing statement.

CodeThe list afterwards
lst [10, 11, 12, 13, 14, 15]
lst[1:3] = [21, 22, 23] [10, 21, 22, 23, 13, 14, 15]
del lst[2:4] [10, 21, 13, 14, 15]
lst[:2] = [] [13, 14, 15]

Exercises

  1. Write a function smooth(a) that takes in a list a of numbers, and then creates and returns a new list with the same number of elements. The first and last element should be the same as in a, while the element at index i should be the mean value of a[i-1], a[i] and a[i+1].

    Example:

     print([1, 2, 6, 4, 5, 0])

    should give us

     [1, 3.0, 4.0, 5.0, 3.0, 0]

    Answer
    def smooth(a):
        res = []
        res.append(a[0])
        for i in range(1, len(a)-1):
            res.append(sum(a[i-1:i+2])/3)
        res.append(a[-1])
        return res
    

Methods and operators that gives us information about lists

Expression Value Comment
a = [3, 9, 2, 7, 9, 2, 3]
a.count(9) 2
a.count('a') 0
a.index(7) 3
a.index(9) 1
a.index(9, 3) 4 Starts looking at index 3.
3 in a True
'x' in a False
'x' not in a True
a = [[1, 1], 1, [1, 2], [1, 1]]
a.count(1) 1 Only counts on the "top" level, so the values within the sublists are disregarded
a.count([1, 1]) 2
a.index([1, 2]) 2

Exercises

  1. Write a function counter2(x, lst) that counts how many times the value x exists on the second level in the given list lst. The top level would be level 1.

    Example: counter2(1, [[[1, 1]], [1, 2, 1], [1, 2]]) should return 3, that is, it should count the red elements.

    Answer
    def counter2(x, lst):
        res = 0
        for l in lst:
            res += l.count(x)
        return res
    
  2. In the function above we assume that the elements in list are lists. What happens if they aren't? Try it out!
  3. We can use the standard function type to examine the type of a value. For example, the expression type(42) == int has the value True, type(42) == list has the value False, and type(['a', 1, [2, 3]]) has the value list.

    Write a function counter(x, lst) that counts how many times the value x exists in a given list lst both on the level 1 and on level 2.

    Example: counter(1, [1, 2, [1, 2, 1], 1, [1, 2]]) should return 5.

    Answer
    def counter(x, lst):
        res = 0
        for l in lst:
            if type(l) == list:
                res += l.count(x)
            elif l == x:
                res += 1
        return res
    

Methods that affects lists

Previously, we've seen how specific list elements can be changed using the assignment operator (e.g. a[3]='hey') and how elements can be added at the end of a list with the method append. There are several other methods that can affect and change lists.

Examples:

CodeThe list's new contentComment
a = [21, 11, 42, 17]
a.extend([0, 3]) [21, 11, 42, 17, 0, 3]
a.pop() [21, 11, 42, 17, 0] pop returns 3.
a.pop(1) [21, 42, 17, 0] pop returns 11.
a.insert(2, 47) [21, 42, 47, 17, 0]
a.append(47) [21, 42, 47, 17, 0, 47]
a.remove(47) [21, 42, 17, 0, 47]
a.reverse() [47, 0, 17, 42, 21]
a.sort() [0, 17, 21, 42, 47]
a.clear() []
a = ['Ola', 'Bo', 'Mi'] ['Ola', 'Bo', 'Mi']
a.sort() ['Bo', 'Mi', 'Ola']
a.append(3) ['Bo', 'Mi', 'Ola', 3]
a.sort() TypeError: '<' not supported
between instances of 'int' and 'str'
The elements must be comparable.
a = [[2, 2, 1], [2, 1], [2, 3]] [[2, 2, 1], [2, 1], [2, 3]]
a.sort() [[2, 1], [2, 2, 1], [2, 3]] Lists are comparable.

Exercises

  1. Assume that lst = ['a', 'b', 'c']. What's the result of lst.append([1, 2]) and lst.extend([1, 2])? Answer
    Try it out!
  2. Write a function extend(lst, x) that does the same thing as lista.extend(x) does but without using the method extend. Assume that x is a list. Answer
    def extend(lst, x):
        for z in x:
            lst.append(z)
    
  3. The method remove(x) removes the first occurrence of the value x at top level. Write a function remove_all(lst, x) that removes all occurrences of x at the top level in the list lst. Answer
    def remove_all(lst, x):
        while x in lst:
            lst.remove(x)
    
    Side note: Since the call lst.remove(x) always starts looking at the beginning of the list this function will be very slow for large lists.

Another way of creating lists

Often we want to build a new list from an old one. In previous examples and exercises we've seen the pattern
new_list = [] for elem in old_list: if elem fulfills a specific criteria: new_list.append[elem]

We used this pattern in the solution of exercise 3 above, where we also noted that the given solution is inefficient for large lists.

There is a much simpler and more efficient way of doing this! To create a new list that contains all the elements except the ones of value x from an old list we can do this:

new_list = [e for e in old_list if e != x]

This is much more efficient since we're only iterating over the list once.

In exercise 3 (remove_all(lst, x)) above, we don't want new list though, we want to change the old one. To do this, we simply assign the variable keeping the old list a new value:

old_list = [e for e in old_list if e != x]

This way to construct lists is called "list comprehension".

More examples:

Expression Value
a = [x for x in range(7)] [0, 1, 2, 3, 4, 6]
[z*z for z in a] [0, 1, 4, 9, 16, 25, 36]
[z*z for z in a if z%2==0] [0, 4, 16, 36]
[round(z/3,2) for z in a] [0.0, 0.33, 0.67, 1.0, 1.33, 1.67, 2.0]

Tuples

Tuples are similar to lists but they are "immutable", that is they can't be changed. Tuples are written with parentheses rather than square brackets. Indexing works the same way as with lists.

Examples:

CodeValueComment
t = (10, 11, 12, 13)
t[-1]13Indexing like in lists.
t[0:3](10, 11, 12)Slicing like in lists.
t.index(12)2Index for first occurrence.
(42) 42Not a tuple! This is a regular int.
(42,)(42,) A tuple with a singular element must be written using a comma.
()()Tuple with zero elements.
t[-1] = 4213Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment
'x', 'a', 'y'('x', 'a', 'y') Can (sometimes) be written without brackets. See the return statement from the exercise with the quadratic equation!
sorted(t, reverse=True)13, 12, 11, 10 Standard functions are alright.

Since tuples are unchangeable only a few of the list methods work: copy, count and index.

So, what's the point of tuples since they don't seem to add any functionality beyond what lists already can handle (everything we do with tuples can be done with lists, but not the other way around). The motivation behind the tuple's existence is that they need fewer resources (memoyy, time) than lists do. We will see more examples of usages in the coming lessons.

Mandatory exercises

  1. Write a function smooth(a, n) that takes in a list of integers a and a non-negative integer n. The function should create a return a new list r where

    ri = (ai-n + ai-n-1 + ai-n-2 + ... + ai-1 + ai + ai+1 + ... + ai+n-1 + ai+n) / (2*n + 1)

    that is, the mean value of ai and its 2n surrounding numbers.

    If n = 1 the result will be as in the exercise (smooth(a)) above, at least for the inner elements.

    For the points close to the corners with very low indexes and very high indexes the interval [i-n:i+n] might be outside the range of the list. In that case, we can choose one of two strategies(You need to do both of them to pass this assignment):

    1. Same breadth (2n+1) should be used in the smoothing operation, but in the elements where index goes below 0 the first value (that is, the one at index 0) should be used, and for elements where the index is too big the last value should be used. We can look at it as extending the lists in both directions with the corner elements.
    2. Only elements that exist in the array should be used, so the mean value is created using fewer elements.

    Example: The code

    x = [1, 2, 6, 4, 5, 0, 1, 2] print('smooth_a(x, 1): ', smooth_a(x, 1)) print('smooth_a(x, 2): ', smooth_a(x, 2)) print('smooth_b(x, 1): ', smooth_b(x, 1)) print('smooth_b(x, 2): ', smooth_b(x, 2))
    should give the following printout
    smooth_a(x, 1): [1.3333333333333333, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.6666666666666667] smooth_a(x, 2): [2.2, 2.8, 3.6, 3.4, 3.2, 2.4, 2.0, 1.4] smooth_b(x, 1): [1.5, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.5] smooth_b(x, 2): [3.0, 3.25, 3.6, 3.4, 3.2, 2.4, 2.0, 1.0]

    Try to use list operators and list methods as much as possible! No if statements are necessary!

    Make sure your functions work for any length of lists, the teacher will test your code with some other lists. Unit tests are available here. If you managed to set up pytest on your computer, please make sure you pass theses tests before asking for evaluation. Instructions to run the tests can be found inside the test file.

  2. Write a function round_list(a_list, ndigits) that returns a new list of the numbers in a_list, but with the numbers rounded up to ndigits decimals. Example: The code
    print('smooth_a(x, 1) rounded: ', round_list(smooth_a(x, 1), 2))
    should, given the same value of x give the printout
    smooth_a(x, 1) rounded: [1.33, 3.0, 4.0, 5.0, 3.0, 2.0, 1.0, 1.67]
    Tip: Use the function round and "list comprehension"!

Question

How many hours did you spend on this lesson?


Go to the next lesson or go back.

Valid CSS!