7 Tuples, Lists, and Dictionaries
Notebook 7 - Lists, Tuples, and Dictionaries¶
Lists, Tuples, and Dictionaries
Make a copy of this notebook by selecting File->Save a copy in Drive from the menu bar above.
Things you'll learn in this lesson:
- mutability
- sequences
- lists
- tuples
- dictionaries
Strings Revisited¶
The len
Function¶
len(string)
returns the length of a passed string
Examples:
print(len('To be or not to be'))
print(len('To be or not to be\n'))
print(len('Tobeornottobe'))
print(len(''))
print(len('To be or not to be'))
print(len('To be or not to be\n'))
print(len('Tobeornottobe'))
print(len(''))
Indexing¶
You can refer to a particular character in a string with this syntax:
string[offset]
Indexing works with variables and literal strings.
In Python, offsets in strings always start counting at zero.
Examples:
mystr = 'marc'
print(mystr[0])
print(mystr[1])
print(mystr[4])
print(mystr[len(mystr)-1])
print('marc'[2])
mystr = 'marc'
print(mystr[0])
print(mystr[1])
print(mystr[4])
print(mystr[len(mystr)-1])
print('marc'[2])
Slicing¶
You can take a subset of a string, we call this 'a slice') using this syntax:
string[beg:end]
the slice starts from offset
beg
(again, counting from 0), and continues through toend-1
Examples:
mystr = 'Python is cool'
print(mystr[0:6])
print(mystr[10:11])
print(mystr[10:10])
print(mystr[10:9])
print('Python is cool'[1:3])
mystr = 'Python is cool'
print(mystr[0:6])
print(mystr[10:11])
print(mystr[10:10])
print(mystr[10:9])
print('Python is cool'[1:3])
ssn = '123-456-7890'
code = input('give me the last four digits of your ssn: ')
if code == ssn[8:]:
print('you are in!!')
else:
print('sorry!')
Default Limits¶
- You can leave out the beg or end part (or both)
- default beg is
0
- default end is
len()-1
Examples:
mystr = 'Python is cool'
print(mystr[0:])
print(mystr[10:])
print(mystr[:14])
print(mystr[:7])
print(mystr[:])
mystr = 'Python is cool'
print(mystr[0:])
print(mystr[10:])
print(mystr[:14])
print(mystr[:7])
print(mystr[:])
Challenge - predict the output from the following print statements:¶
mystr = 'Python is cool'
print(mystr[7])
print(mystr[len(mystr)])
print(mystr[1:15])
print(mystr[1:1])
print(mystr[-5:-1])
print(mystr[-1:-5])
print(mystr[0:])
print(mystr[:-1])
mystr = 'Python is cool'
print(mystr[7])
print(len(mystr))
print(mystr[len(mystr)-1])
print(mystr[1:15])
print(mystr[1:1])
print(mystr[-5:-1])
print(mystr[-1:-5])
print(mystr[0:])
print(mystr[:-1])
The in
Operator¶
in
is a boolean operator which, when used with strings, tests whether a substring exists in another string- the result is a boolean value (True or False)
Examples:
print('a' in 'Marc')
print('arc' in 'Marc')
print('m' in 'Marc')
print('Marc' in 'Marc')
print('' in 'Marc')
print('a' in 'Marc')
print('arc' in 'Marc')
print('m' in 'Marc')
print('Marc' in 'Marc')
print('' in 'Marc')
String Methods¶
- everything in Python is an object
- strings (and other objects) have special functions attached to them which we call "methods"
- string methods are invoked like this:
"marc".capitalize()
- usually, you'll use a variable rather than a string literal, like this:
name.capitalize()
name = 'Marc Cohen'
print(len(name))
print(name.upper())
print(name.lower())
find()
returns the offset of the first matching substringfind()
returns -1 if no match is found
Example:
mystr = 'bookkeeper'
print(mystr.find('ookkee'))
print(mystr.find('okay'))
mystr = 'bookkeeper'
print(mystr.find('ookkee'))
print(mystr.find('okay'))
print(mystr.find('k'))
print(mystr.find(''))
String Immutability¶
- strings cannot change after they are created
- we call this property “immutability”
- strings are said to be “immutable” (i.e. unchanging)
- string methods which appear to change the string actually return a new string
For example, the upper()
string method:
name = 'Marc'
upper_name = name.upper()
print(f'name={name} still exists, upper_name={upper_name} is a new string')
creates and returns a new string. The original string, the one we called the method on, returns a new string.
name = 'Marc'
upper_name = name.upper()
print(f'name={name} still exists, upper_name={upper_name} is a new string')
String Editing¶
the replace() string method takes these arguments:
- a substring to replace
- a replacement string
- an optional maximum number of occurrences to replace (default is all)
Example: replace every zero with two zeros...
salary = '1000'
new_salary = salary.replace('0', '00')
print(new_salary)
salary = '1000'
new_salary = salary.replace('0', '00')
print(new_salary)
Example: improve your reviews...
feedback = input('How am I doing? ')
better_feedback = feedback.replace('worst', 'best')
print(better_feedback)
feedback = input('How am I doing? ')
improved_feedback = feedback.replace('worst', 'best')
print(improved_feedback)
Mutability¶
Some data types can be changed (mutable) and some cannot. For example, strings are immutable. For this reason, you cannot assign values to the characters in a string. And when you call a function that seems to modify a string, it actually creates a new string and (normally) disposes of the old one.
s = 'Susan X. Anthony'
print('sixth char:', s[6])
s[6] = 'B'
print(s)
s = 'Susan B. Anthony'
print(s)
Sequences¶
Sequences are any data type that contained an ordered collection of objects. We've already seen one sequence: strings contain an ordered collection of characters.
Many of the operations we can do on strings, for example:
- indexing:
mystr[7]
- slicing:
mystr[3:5]
- iterating:
for i in mystr:
- taking the length:
len(mystr)
- the
in
operator:if x in mystr
can also be done on other types of sequences.
In this lesson, we'll learn about some new types of sequences:
- lists
- tuples
- dictionaries
Lists¶
- Lists are ordered sequences.
- All the sequence operations you learned about with strings, like
len
, indexing, slicing, loops, thein
operator, etc. apply to lists as well.
Lists are ordered collections, i.e. sequences, like strings, but they can contain any type of value (not just characters). They can even contain different types within the same list. Lists are defined inside square brackets, with list elements separated by commas, for example...
['a', 'b', 'c', 1, 2, 3]
Creating Lists¶
# Create an empty list (lists use square brackets instead of parens)
li = []
print('empty list:', li)
# Create and initialize a list with some data
li = ['Python', 123, 3.14, True]
print('non-empty list:', li)
# the same value can occur multiple times in a list
li = ['a', 'a', 'a']
print(li)
List Operations¶
# The len() function gives us the size of a list.
li = ['abc', 123, 3.14, True, 5]
# get the size of a list
list_size = len(li)
print(list_size)
li = ['abc', 123, 3.14, True, 99, 101, "another", "last one - I promise"]
# iterate (loop) over the elements in a list
for i in range(len(li)):
print(li[i])
# A better way to iterate over the elements in a list
li = ['abc', 123, 3.14, True, 99, 101, "another", "last one - I promise"]
for i in li:
print(i)
li = ['abc', 123, 3.14, True]
# test membership in a list
x = 3.14159
if not x in li:
print(x, ‘is not in list’)
else:
print(x, ‘is in list’)
li = ['abc', 123, 3.14, True]
# indexing (list indexes start with zero!)
print(li[2])
# indexing out of bounds raises a runtime error
li = ['abc', 123, 3.14, True]
print(li[99])
li = ['abc', 123, 3.14, True]
# slicing
print(li[1:3])
# concatenating lists
li1 = ['a', 1]
li2 = ['b', 2]
li3 = [99.99]
li4 = li1 + li2 + li3
print(li4)
Lists are Mutable¶
Unlike strings, we can change the contents of a list after it's created.
# add an element
li = []
print(li)
li.append('elem1')
print(li)
li.append(400)
print(li)
li = ['elem1', 'elem2']
print(li)
# remove an element
li.remove('elem1')
print(li)
# removes only first occurrence of 'element' in list
# differs from del because it’s based on value, not position
li = ['elem1', 'elem2', 3, 99.9]
# remove & retrieve an element based on position
print(‘start:’, li)
for i in range(len(li)):
elem = li.pop()
print(‘removed’, elem, ‘remaining list:’, li)
# If you remove an element that doesn't exist, Python gives a run time error.
# How can that be avoided? Test for existence before removing by value
li = ['elem1', 'elem2']
e = 'elem3'
li.remove(e)
li = ['elem1', 'elem2', 'elem3']
print(li)
# replace an element by index
li[1] = 'foo' # overwrites value at index 2
print(li)
li = ['abc', 123, 3.14, True]
print(li[1:3])
# assign to a list slice
li[1:] = [9,8,7.6]
print(li)
li = ['abc', 123, 3.14, True]
# delete a list element
del li[2]
print(li)
li = ['abc', 123, 3.14, True]
# delete a list slice
del li[1:3]
print(li)
li = ["Maya", "Marc", "Kimba"]
print('original:', li)
# sorting a list
li.sort()
print(‘sorted:’, li)
# sorts in ascending order (small to large) by default
# to sort in descending order, use this syntax:
li.sort(reverse=True)
print(‘reverse sorted:’, li)
li.sort()
print(’re-sorted:’, li)
li = ['abc', 123, 3.14, 'abc', 'abc', True, 'abc', 123]
# get the number of occurrences of a particular value
count = li.count('abc')
print(count)
li = ['abc', 123, 3.14, True, 123]
# get the (first) index of a particular value
index = li.index(True)
print(index)
li = ['abc', 123, 3.14, True, 123]
# reverse a list
li.reverse()
print(li)
li.reverse()
print(li)
Nested Lists¶
Just as you can have if statements of if statements (nested if statements), and loops of loops (nested loops), we can also have also have lists of lists.
- list of lists:
[[1, 2], [3, 4]]
List Example¶
Imagine I want to maintain a list of students and their quiz scores. If I think about just one particular student, I might like to store the student's name and each quiz score up to the current lesson. I'd want to use a list because I'm going to want to add quiz results every week and, occasionally, I might need to change a grade.
Here's the data for one student...
student = [ 'Kimba', 95, 100, 90 ]
print(student)
But we have many students, so I need to store one instance of that list for every student. That collection also needs to be mutable, because I may need to add or delete students over time.
This seems like a job for a list of lists, like this one...
students = [
[ 'Kimba', 95, 100, 90 ],
[ 'Maya', 90, 95, 100 ],
[ 'Marc', 85, 90 ]
]
print(students)
We can access a sublist, i.e. one of the lists in this list of lists, like this...
students = [
[ 'Marc', 95, 100, 90 ],
[ 'Maya', 90, 95, 100 ]
]
student = students[0] # get data for Marc
print(student)
We can access an element of a sublist, like this...
# get Maya's second test score
students = [
[ 'Marc', 95, 100, 90 ],
[ 'Maya', 90, 95, 100 ]
]
score = students[1][2]
print(score)
Let's now generate a table of average scores for each student, effectively each student's final grade...
students = [
[ 'Kimba', 95, 100, 90, 98, 95, 99, 93, 97 ],
[ 'Marc', 90, 95, 100, 90, 100, 93, 74 ],
[ 'Maya', 90, 80, 100, 99, 815]
]
#print(students)
for student in students:
total = 0
count = len(student)
for score in student[1:]:
total += score
average = total / (count - 1)
print(student[0], average)
Tuples¶
- A tuple is like a list but it's immutable (unchangeable).
- Almost everything you know about lists also applies to tuples.
Tuples are defined by parentheses (i.e. brackets) and the elements of a tuple are separated by commas, like this:
('a', 'b', 'c', 1, 2, 3)
.
Creating Tuples¶
# Create an empty tuple
tup = ()
print('empty tuple:', tup)
# Create a tuple with some initial contents
tup = ('Python', 1024, 3.14, True)
print('non-empty tuple:', tup)
# the same value can occur multiple times in a tuple
tup = ('a', 'a', 'a')
print(tup)
Tuple Operations¶
Because tuples are sequences, like strings and lists, functions and for
loops that operate on strings also play well with tuples, for example:
# the len function returns the length of a tuple
tup = (1, 2, 3, 4)
print(len(tup)) # returns size of a tuple
tup = (1, 2, 3, 4, 5)
print(len(tup))
# indexing (like strings, tuple offsets start with zero!)
tup = (1, 2, 3)
print(tup[0])
print()
print('the whole tuple...')
print(tup)
# indexing out of bounds raises a runtime error
tup = ('abc', 123, 3.14, True)
print(tup[99])
# loops
tup = (1, 2, 3)
for i in range(len(tup)):
print(tup[i])
# a better way to loop (a.k.a. iterate) over tuples...
tup = (1, 2, 3)
for i in tup:
print(i)
# the in operator (membership test)
x = 4
tup = (1, 2, 3, 4, 5)
if x in tup: # True if var’s value is in tuple
print(tup, 'contains ', x)
else:
print(tup, 'does NOT contain', x)
# slicing
tup = (1, 2, 3, 4, 5)
print(tup[2:]) # prints 3rd through end of tuple
print(tup[:3]) # prints first through third from last
print(tup[2:4])
# the plus operator concatenates (combines) two tuples into one
t1 = (1, 2, 3)
t2 = (4, 5, 6)
print(t1 + t2)
Tuples are Immutable¶
Like strings, once created, you can't change a tuple.
tup = (1, 2, 3)
print(tup[1])
tup[1] = 7
But you can assign a new tuple to the same variable. You haven't changed the tuple, you've changed the association between a variable and it's value.
tup = (1, 2, 3)
print(tup)
tup = (1, 3, 2)
# the tuple didn't change, the tup variable now points to different data!
print(tup)
Nested Tuples¶
Just as we saw lists of lists, we can also have also have tuples of tuples. In fact, we can even have lists of tuples and tuples of lists!
- tuple of tuples:
((1, 2), (3, 4))
- tuple of lists:
([1, 2], [3, 4])
- list of tuples:
[(1, 2), (3, 4)]
We can even have lists of lists of tuples of lists of strings... you get the idea, this can get arbitrarily complex. Fortunately, most of the time you only need to use one or two levels, although occasionally you may need to go deeper.
# Tuple of tuples of tuples (3 level nesting)
nested_tuple = (
(
(1, 2),
(3, 4)
),
(
(5, 6),
(7, 8)
)
)
print(nested_tuple)
Dictionaries¶
A dictionary is an organized collection of key/value pairs. The data is organized for quick access via the key, somewhat like a real dictionary, where words are the keys and their definitions are the associated values.
Dictionaries are defined using curly braces with key:value pairs separated by colons, like this:
websites = {
'google': 'https://google.com',
'youtube': 'https://youtube.com',
'baidu': 'https://baidu.com',
}
This data type is known by various names in other languages:
- map (C++)
- hashmap (Java)
- associative array (generic term)
This object type is extremely powerful for representing indexed data. The keys in a dictionary are arranged to facilitate fast lookup by key value.
They are optimized for direct, not sequential, access
There is no implied order of keys or values
You can't index a dictionary by position
But you can index dictionaries by key value, as we’ll see
You can't take slices of a dictionary
Dictionaries are mutable, like lists, they can grow, shrink, or change over time
Dictionary keys are immutable (e.g., string, number, tuple) because changing keys on the fly would confuse the dictionary.
Dictionary values can have any type (mutable or immutable).
Dictionary Operations¶
# Create an empty dictionary (use curly braces instead of parens or square brackets)
grades = {}
print(grades)
# Create and initialize a dictionary
grades = { 'Marc' : 95, 'Maya': 100 }
print(grades)
# If the same key occurs multiple times, python only keeps the last value
x = { 'a' : 1, 'a' : 2 }
print(x)
# but the same value may appear any number of times.
x = { 'a' : 1, 'b' : 1 }
print(x)
# Get the size of a dictionary (returns number of key/value pairs)
grades = { 'Marc' : 95, 'Maya': 100 }
print(len(grades))
# Retrieve the value associated with a given key
grades = { 'Marc' : 95, 'Maya': 100 }
grade = grades['Maya']
print(grade)
# The value inside the square brackets may be a literal, a variable or any
# arbitrary expression. Similar syntax to list/tuple indexing but key based,
# not positional.
grades = { 'Marc' : 95, 'Maya': 100 }
# Attempting to retrieve a non-existent key causes an error
x = 'Marc'
grades[x]
grades = { 'Marc' : 95, 'Maya': 100 }
student = 'foo'
# play it safe by testing for key existence before access
grade = grades[student]
if student in grades:
grade = grades[student]
print('grade for', student, 'is', grade)
else:
print('student', student, 'not found')
# When used with dictionaries, the in operator only checks the existence
# of keys, not values. You can also use “not in” to test for non-existence
# of a key.
grades = { 'Marc' : 95, 'Maya': 100, 'Kimba': 85 }
# loop through a dictionary (this iterates over the dictionary keys)
for i in grades:
print(i, grades[i])
grades = { 'Marc' : 95, 'Maya': 100, 'Kimba': 85 }
# By default, the keys will appear in random order. You can iterate keys in order by sorting them first:
for i in sorted(grades):
print(i, grades[i])
Dictionaries are Mutable¶
grades = { 'Marc' : 95, 'Maya': 100 }
print('before:', grades)
#Add a key/value pair
grades['Kimba'] = None # new, no grade yet
print('after:', grades)
grades = { 'Marc' : 95, 'Maya': 100, 'Kimba': None }
print('before:', grades)
grades['Marc'] = 80 # grade recorded
print('after1:', grades)
grades['Marc'] += 5 # increment Marc's grade
print('after2:', grades)
grades['Kimba'] = 99 # change Kimba's grade
print('after2:', grades)
grades = { 'Marc' : 95, 'Maya': 100, 'Kimba': 85 }
# delete a key/value pair
del grades['Marc'] # Marc dropped the course
print(grades)
grades = { 'Marc' : 95, 'Maya': 100, 'Kimba': 85 }
# Trying to delete a key that doesn't exist will cause a runtime error.
del grades['Fred']
Nested Dictionaries¶
Just like we can have nested if statements, nested loops, nested tuples, and nested lists, we can also have nested dictionaries.
- dictionary of tuples:
{'key1': (1,2), 'key2': (3,4)}
- dictionary of lists:
{'key1': [1,2], 'key2': [3,4]}
- dictionary of dictionaries:
{
'key1' : {
'key1' : [1, 2],
'key2' : [3, 4]
}
'key2' : {
'key1' : [1, 2],
'key2' : [3, 4]
}
}
This can get arbitrarily complex (dictionaries of lists of tuples of dictionaries of...).
Dictionary Example¶
Once again, imagine I want to maintain a list of students and their quiz scores. If I think about just one particular student, I might like to store the student's name and each quiz score up to the current lesson. Before we used a list like this:
student = [ 'Jeff', 95, 100, 90 ]
But I'd like to organize the test scores by student name so that I can efficiently find any given student's scores by their name (i.e. by indexing on the dictionary key).
This leads us to a dictionary of lists:
grades = {
'Marc' : [95, 100, 90],
'Maya' : [90, 95, 100]
}
To add a student:
grades[student] = []
To delete a student:
del grades[student]
To add a new score for a student:
grades[student].append(new_score)
To replace the 2nd quiz score for a student:
grades[student][1] = new_score
Rule of thumb for truth value of tuples, lists, and dictionaries¶
All of these objects may be used as boolean values. The rules for converting a tuple, list, or map into a boolean value are as follows:
- if the object is empty, it evaluates to False
- if the object is non-empty, it evaluates to True
print("tuple test...\n")
for i in (), (1,2,3), ('a', 1), (None,):
print(f"{i} is {bool(i)}")
print('list test...')
for i in [], [1,2,3], ['a', 1], [None]:
print(f"{i} is {bool(i)}")
print('dictionary test...')
for i in {}, {1: 2, 3: 4}, {'a':1, 'b':2, 'c':3}, {None:None}:
print(f"{i} is {bool(i)}")
Homework¶
Question 1¶
I have a list of things I need to buy from my supermarket of choice.
I want to know what the first thing I need to buy is. However, when I run the program it shows me a different answer than what I was expecting. What is the mistake? Can you fix it in the cell below?
shopping_list = [
"oranges",
"cat food",
"sponge cake",
"long-grain rice",
"cheese board",
]
print(shopping_list[1])
#@title Double click here to reveal solution
print(shopping_list[0]) # because sequence indexes start with zero
Question 2¶
I'm setting up my own market stall to sell chocolates. I need a basic till to check the prices of different chocolates that I sell. I've started the program and included the chocolates and their prices. Finish the program by asking the user to input an item and then output its price.
chocolates = {
'white': 1.50,
'milk': 1.20,
'dark': 1.80,
'vegan': 2.00,
}
# Add your code here.
#@title Double click here to reveal solution
chocolates = {
‘white’: 1.50,
‘milk’: 1.20,
‘dark’: 1.80,
‘vegan’: 2.00,
}
while True:
item = input(“Enter the desired item (q to quit): “).lower()
if item == ‘q’:
break
if item in chocolates:
print(‘The price for {} chocolate is £{:.2f}.\n’.format(item, chocolates[item]))
else:
print(‘Sorry, we don't sell {} chocolate.\n’.format(item))
Question 3¶
Write a function named enum()
that takes a list of strings and enumerates them, i.e. it prints each element on a separate line, prefixed by it's index. For example...
li = ['item', 'another item', 'last item']
enum(li)
should produce this output:
1. item
2. another item
3. last item
#@title Double click here to reveal solution
def enum(li):
index = 0
for i in li:
print(f"{index}: {i}")
index += 1
li = [‘item’, ‘another item’, ‘last item’]
enum(li)
Question 4¶
Write a program that simulates a lottery. The program should have a list of seven numbers that represent a lottery ticket. It should then generate seven random numbers. After comparing the two sets of numbers, the program should output a prize based on the number of matches:
- £1 for one matching number
- £10 for two matching numbers
- £100 for three matching numbers
- £1000 for four matching numbers
- £10000 for five matching numbers
- £100000 for six matching numbers
- £1000000 for seven matching numbers
# Add your code here
#@title Double click here to reveal solution.
import random
my_numbers = random.choices(range(1, 99), k=7)
print(f“your numbers are: {my_numbers}")
winning_numbers = random.choices(range(1, 99), k=7)
print(f“winning numbers are: {winning_numbers}")
matches = 0
for i in my_numbers:
if i in winning_numbers:
matches += 1 # shorthand for hits = hits + 1
prizes = {
1: 1,
2: 10,
3: 100,
4: 1000,
5: 10000,
6: 100000,
7: 1000000
}
print()
if matches in prizes:
print(f”** You matched {matches} numbers and won £{prizes[matches]}! **")
else:
print(“Sorry, no prize won, better luck next time!")