Lists & Dictionaries

Overview

Teaching: 60 min
Exercises: 30 min
Questions
  • How can I store multiple values?

Objectives
  • Explain why programs need collections of values.

  • Write programs that create flat lists, index them, slice them, and modify them through assignment and method calls.

A list stores many values in a single structure.

pressures = [0.273, 0.275, 0.277, 0.275, 0.276]
print('pressures:', pressures)
print('length:', len(pressures))
pressures: [0.273, 0.275, 0.277, 0.275, 0.276]
length: 5

Use an item’s index to fetch it from a list.

print('zeroth item of pressures:', pressures[0])
print('fourth item of pressures:', pressures[4])
zeroth item of pressures: 0.273
fourth item of pressures: 0.276

Lists’ values can be replaced by assigning to them.

pressures[0] = 0.265
print('pressures is now:', pressures)
pressures is now: [0.265, 0.275, 0.277, 0.275, 0.276]

Appending items to a list lengthens it.

primes = [2, 3, 5]
print('primes is initially:', primes)
primes.append(7)
primes.append(9)
print('primes has become:', primes)
primes is initially: [2, 3, 5]
primes has become: [2, 3, 5, 7, 9]
teen_primes = [11, 13, 17, 19]
middle_aged_primes = [37, 41, 43, 47]
print('primes is currently:', primes)
primes.extend(teen_primes)
print('primes has now become:', primes)
primes.append(middle_aged_primes)
print('primes has finally become:', primes)
primes is currently: [2, 3, 5, 7, 9]
primes has now become: [2, 3, 5, 7, 9, 11, 13, 17, 19]
primes has finally become: [2, 3, 5, 7, 9, 11, 13, 17, 19, [37, 41, 43, 47]]

Note that while extend maintains the “flat” structure of the list, appending a list to a list makes the result two-dimensional.

Use del to remove items from a list entirely.

print('primes before removing last item:', primes)
del primes[4]
print('primes after removing last item:', primes)
primes before removing last item: [2, 3, 5, 7, 9]
primes after removing last item: [2, 3, 5, 7]

The empty list contains no values.

Lists may contain values of different types.

goals = [1, 'Create lists.', 2, 'Extract items from lists.', 3, 'Modify lists.']

Character strings can be indexed like lists.

element = 'carbon'
print('zeroth character:', element[0])
print('third character:', element[3])
zeroth character: c
third character: b

Character strings are immutable.

element[0] = 'C'
TypeError: 'str' object does not support item assignment

Indexing beyond the end of the collection is an error.

print('99th element of element is:', element[99])
IndexError: string index out of range

Fill in the Blanks

Fill in the blanks so that the program below produces the output shown.

values = ____
values.____(1)
values.____(3)
values.____(5)
print('first time:', values)
values = values[____]
print('second time:', values)
first time: [1, 3, 5]
second time: [3, 5]

Solution

values = []
values.append(1)
values.append(3)
values.append(5)
print('first time:', values)
values = values[1:]
print('second time:', values)

How Large is a Slice?

If ‘low’ and ‘high’ are both non-negative integers, how long is the list values[low:high]?

Solution

The list values[low:high] has high - low elements. For example, values[1:4] has the 3 elements values[1], values[2], and values[3]. Note that the expression will only work if high is less than the total length of the list values.

From Strings to Lists and Back

Given this:

print('string to list:', list('tin'))
print('list to string:', ''.join(['g', 'o', 'l', 'd']))
['t', 'i', 'n']
'gold'
  1. Explain in simple terms what list('some string') does.
  2. What does '-'.join(['x', 'y']) generate?

Solution

  1. list('some string') “splits” a string into a list of its characters.
  2. x-y

Working With the End

What does the following program print?

element = 'helium'
print(element[-1])
  1. How does Python interpret a negative index?
  2. If a list or string has N elements, what is the most negative index that can safely be used with it, and what location does that index represent?
  3. If values is a list, what does del values[-1] do?
  4. How can you display all elements but the last one without changing values? (Hint: you will need to combine slicing and negative indexing.)

Solution

The program prints m.

  1. Python interprets a negative index as starting from the end (as opposed to starting from the beginning). The last element is -1.
  2. The last index that can safely be used with a list of N elements is element -N, which represents the first element.
  3. del values[-1] removes the last element from the list.
  4. values[:-1]

Stepping Through a List

What does the following program print?

element = 'fluorine'
print(element[::2])
print(element[::-1])
  1. If we write a slice as low:high:stride, what does stride do?
  2. What expression would select all of the even-numbered items from a collection?

Solution

The program prints

furn
eniroulf
  1. stride is the step size of the slice
  2. The slice 1::2 selects all even-numbered items from a collection: it starts with element 1 (which is the second element, since indexing starts at 0), goes on until the end (since no end is given), and uses a step size of 2 (i.e., selects every second element).

Slice Bounds

What does the following program print?

element = 'lithium'
print(element[0:20])
print(element[-1:3])

Solution

lithium

Sort and Sorted

What do these two programs print? In simple terms, explain the difference between sorted(letters) and letters.sort().

# Program A
letters = list('gold')
result = sorted(letters)
print('letters is', letters, 'and result is', result)
# Program B
letters = list('gold')
result = letters.sort()
print('letters is', letters, 'and result is', result)

Solution

Program A prints

letters is ['g', 'o', 'l', 'd'] and result is ['d', 'g', 'l', 'o']

Program B prints

letters is ['d', 'g', 'l', 'o'] and result is None

sorted(letters) returns a sorted copy of the list letters (the original list letters remains unchanged), while letters.sort() sorts the list letters in-place and does not return anything.

Copying (or Not)

What do these two programs print? In simple terms, explain the difference between new = old and new = old[:].

# Program A
old = list('gold')
new = old      # simple assignment
new[0] = 'D'
print('new is', new, 'and old is', old)
# Program B
old = list('gold')
new = old[:]   # assigning a slice
new[0] = 'D'
print('new is', new, 'and old is', old)

Solution

Program A prints

new is ['D', 'o', 'l', 'd'] and old is ['D', 'o', 'l', 'd']

Program B prints

new is ['D', 'o', 'l', 'd'] and old is ['g', 'o', 'l', 'd']

new = old makes new a reference to the list old; new and old point towards the same object.

new = old[:] however creates a new list object new containing all elements from the list old; new and old are different objects.

Dictionaries let you set up a key/value store

The two main ways of creating a dict, one uses braces and colons, the other uses keyword arguments to the dict built-in function:

# This is one way:

dict1 = {'foo': 'bar', 'kittens': 5}

dict1
{'foo': 'bar', 'kittens': 5}
# This does the same thing:

dict2 = dict(foo='bar', kittens=5)

dict2
{'foo': 'bar', 'kittens': 5}

Accessing dicts is now done through the string keys:

dict1['foo']
'bar'

We can easily add items to our dictionary after-the-fact:

dict1['fruits'] = ['bananas', 'apples']

dict1
{'fruits': ['bananas', 'apples'], 'foo': 'bar', 'kittens': 5}

We added a value to the dictionary that is a list. The combination of lists and dictionaries is a powerful way of describing data:

data = {
     'servers': [
         dict(name='Beluga',
              location='Montreal',
              cores_per_node=40),
         dict(name='Cedar',
              location='Burnaby',
              cores_per_node=32)
     ],
     'sandwiches': ['Ham', 'Po boy', 'PB & J', 'Bánh Mì']
}

data
{'sandwiches': ['Ham', 'Po boy', 'PB & J', 'B\xc3\xa1nh M\xc3\xac'], 'servers': [{'cores_per_node': 40, 'location': 'Montreal', 'name': 'Beluga'}, {'cores_per_node': 32, 'location': 'Burnaby', 'name': 'Cedar'}]}

We can now traverse this datastructure to access individual pieces of data, e.g.,

data['servers'][1]['name']
'Cedar'
data['sandwiches'][2]
'PB & J'

The keys method gives us a list of the dictionary’s keys:

data.keys()
['sandwiches', 'servers']

The values method gives us a list of the dictionary’s values:

data.values()
[['Ham', 'Po boy', 'PB & J', 'B\xc3\xa1nh M\xc3\xac'], [{'cores_per_node': 40, 'location': 'Montreal', 'name': 'Beluga'}, {'cores_per_node': 32, 'location': 'Burnaby', 'name': 'Cedar'}]]

Trying to access a key that doesn’t exist throws an error:

data['shirts']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'shirts'

We can test if a key exists though without causing an error:

'shirts' in data
False
'sandwiches' in data
True

Key Points

  • A list stores many values in a single structure.

  • Use an item’s index to fetch it from a list.

  • Lists’ values can be replaced by assigning to them.

  • Appending items to a list lengthens it.

  • Use del to remove items from a list entirely.

  • The empty list contains no values.

  • Lists may contain values of different types.

  • Character strings can be indexed like lists.

  • Character strings are immutable.

  • Indexing beyond the end of the collection is an error.

  • A dictionary (dict) is a key value store