Lecture 04 Python Data Types 2
Lecture 04 Python Data Types 2
Gabriele Tolomei
gtolomei@[Link]
University of Padua, Italy
2018/2019
October 22, 2018
Lecture 4: Python's Built-in Data Types (2)
Data Type Hierarchy
Python's built-in data types can be grouped into several classes.
Operations de ned on lists are the same we have already seen for any
other sequence type (e.g., str ).
Operations
In [1]: # Define a reference to an empty list object
a_list = []
# Rebind the above reference to another list object
a_list = [1, 2, 'foo', None]
# Print the length of the list
print(len(a_list))
# Access the ith element of the list ﴾remember, the first element is indexed by 0﴿
print(a_list[2])
# a_list[i] stands for a_list[ni], where n = len﴾a_list﴿
# In the example below, therefore, we are accessing the very last element of the list
print(a_list[1]) # same as a_list[len﴾a_list﴿1]
# Change an element of the list ﴾in place﴿
a_list[1] = 'bar'
print(a_list)
# Trying to access an element outside of the index range
print(a_list[7])
4
foo
None
[1, 'bar', 'foo', None]
IndexError Traceback (most recent call last)
<ipythoninput14bc7f67583df> in <module>()
14 print(a_list)
15 # Trying to access an element outside of the index range
> 16 print(a_list[7])
[[4, 5], 1, 'bar', 'red', 'foo', None, 42, 73, 'blue', 'cyan']
Notes on insert vs. append
insert(pos, element) is computationally expensive compared to
append(element) .
cyan
[[4, 5], 1, 'bar', 'red', 'foo', None, 42, 73, 'blue']
[[4, 5], 1, 'bar', 'foo', None, 42, 73, 'blue']
Checkpoint Quiz
What happens if we try to do the following a_list.pop(123) , namely if we
try to pop out an element using an out-of-range index?
In [5]: # Trying to pop out an element using an outofrange index
a_list.pop(123)
IndexError Traceback (most recent call last)
<ipythoninput59312c34ca1c2> in <module>()
1 # Trying to pop out an element using an outofrange index
> 2 a_list.pop(123)
ValueError Traceback (most recent call last)
<ipythoninput6bce0b8060d6c> in <module>()
11 print(a_list)
12 # What if we try to remove an element which is not in the list?
> 13 a_list.remove('baz')
14 print(a_list)
Out[7]: False
Notes on the usage of in with lists
Checking whether a list contains an element is a lot slower than using
dict and sets (to be introduced shortly).
Using list , Python has to make a linear scan across the values of the
list (time complexity is if is the number of elements of the list).
Using dict and sets - which are based on hash tables - can make the
check in constant time, i.e., .
List Concatenation
In [8]: # Lists can be added together using '+'
[42, True, 'bar'] + [False, None, 'foo'] + ['baz', 48, '']
Out[8]: [42, True, 'bar', False, None, 'foo', 'baz', 48, '']
In [9]: # If you have a list already defined,
# you can append multiple elements to it using 'extend' in place.
a_list = [42, True, 'bar']
print(a_list)
a_list.extend([False, None, 'foo'])
print(a_list)
a_list.extend(['baz', 48, ''])
print(a_list)
A new list must be created and the objects copied over for each
concatenation (similar to string concatenation).
[7, 2, 5, 1, 3]
[1, 2, 3, 5, 7]
['saw', 'small', 'He', 'foxes', [123], 'six']
[[123], 'He', 'saw', 'six', 'small', 'foxes']
Notes on sorting
Whenever you sort a list using sort() , remember that this happens in-
place (i.e., you can not recover the original order).
If you want to display a list in sorted order, but preserve the original
order, you can use the sorted() function, instead.
# Display students in reverse alphabetical order, but keep the original order.
print("Here is the list in reverse alphabetical order:")
print(sorted(students, reverse=True))
[2, 3, 7, 8]
Notes on slicing
Element at the start index is included, whilst the stop index is not.
[7, 2, 3, 7, 8]
[8, 6, 0, 1]
[6, 0, 1]
[2, 3, 7, 8, 6]
In [14]: print(a_list)
# A step can also be used after a second colon to, say, take every other element
print(a_list[::2])
# A clever use of this is to pass 1, which has the useful effect of reversing the list
print(a_list[::1])
[7, 2, 3, 7, 8, 6, 0, 1]
[7, 3, 8, 0]
[1, 0, 6, 8, 7, 3, 2, 7]
Again on slicing
Looping over a List
Accessing all the elements in a list
One of the most important concepts related to lists.
We use a loop (more on this later) to access all the elements in a list.
A loop is a block of code that repeats itself until it runs out of items to
work with, or until a certain condition is met.
In this case, our loop will run once for every item in our list (e.g., if a list
has three items, our loop will run three times).
In [15]: # Define a list containing dog breeds
dogs = ['border collie', 'golden retriever', 'german shepherd']
# Print each dog breed contained in the list above
for dog in dogs:
print(dog)
border collie
golden retriever
german shepherd
How does looping work?
The keyword for tells Python to "get ready" to use a loop.
['banana', 'papaya']
In [19]: # List comprehension allows us to write the same thing yet in a more compact way
# Let's start from scratch with an empty list ﴾this step is not really needed﴿
result = []
# Using list comprehension you can do it in just a single line!
result = [word for word in words if [Link]('a') >= 2]
# Finally, print the result
print(result)
['banana', 'papaya']
In [20]: # Note that list comprehension works also when you have nested lists
# For example, consider the following list of lists
data = [['banana', 'kiwi', 'apple', 'melon'],['pineapple', 'papaya', 'strawberry', 'mango']]
# If you want to obtain a list of words starting with the letter 'm'
words_starting_with_m = [word for word_list in data for word in word_list
if [Link]('m')]
# Finally, print the final list
print(words_starting_with_m)
['melon', 'mango']
Tuples: Type tuple (immutable)
Properties
Tuples are basically immutable lists.
Lists are great for containing highly dynamic information, as you can
append/insert/remove/modify items in a list.
However, sometimes we may want to ensure that no user nor part of a
program can change a list. That's exactly what tuples are for!
Allowed operations are the same as those of any other sequence type
(i.e., list , str , bytes , etc.).
In [21]: # Defining a tuple is like defining a list, except you use parentheses
# instead of square brackets
colors = ('red', 'green', 'blue')
# Once you have a tuple, you can access individual elements just like you can with a list...
print('The second color is: ' + colors[1])
# ... and you can loop through the tuple with a for loop:
print('\nHere is the list of primary colors:')
for color in colors:
print([Link]())
AttributeError Traceback (most recent call last)
<ipythoninput2136c2c5feb193> in <module>()
10
11 # What happens if we try to add an item to the tuple?
> 12 [Link]('black')
AttributeError Traceback (most recent call last)
<ipythoninput22f763f168a5eb> in <module>()
> 1 [Link]()
Source: Wikipedia
Lookup
To determine if an object is in a hash table, we only have to hash the
object, and look in the bucket corresponding to that hash.
This is a (i.e., constant time) operation which does not depend on
the size of the input
Of course, assuming the hash function evenly distributes objects in the
available buckets (collisions).
Hashing in Python
Python has a built-in function that performs a hash called hash() .
Hash of 42 is: 42
Hash of "Aloha" is: 7768763457878087930
Hash of empty tuple () is: 3527539
In [25]: # Not every Python object is hashable!
# Hashing a list ﴾mutable﴿
print("Hash of list [1, 3, 5] is: {}".format(hash([1, 3, 5])))
TypeError Traceback (most recent call last)
<ipythoninput25ea2ff4dcf778> in <module>()
1 # Not every Python object is hashable!
2 # Hashing a list (mutable)
> 3 print("Hash of list [1, 3, 5] is: {}".format(hash([1, 3, 5])))
A set can be created in two ways: using a set literal with curly braces or
via the set function.
In [26]: # Defining a set using curly braces
s = {3,5,6,5,5,2,1,4,3}
print(s)
# Defining a set using the 'set' builtin function
s = set([3,5,6,5,5,2,1,4,3])
print(s)
# Note that this means that we can also transform a list into a set
a_list = ['apple', 'kiwi', 'banana', 'apple', 'ananas', 'kiwi', 'pear', 'apple']
s = set(a_list)
print(s)
{1, 2, 3, 4, 5, 6}
{1, 2, 3, 4, 5, 6}
{'pear', 'kiwi', 'apple', 'banana', 'ananas'}
In [27]: """
Note that the following is legitimate because the objects used to create the set s
can be stored in an 'iterable' (like the mutable list below),
provided that each individual element in the iterable is hashable
(i.e., immutable) like the integers below.
"""
s = set([3,5,6,5,5,2,1,4,3])
print(s)
"""
But what if I do the following?
"""
s = set([[3,5,6,5],[5,2,1,4,3]])
print(s)
{1, 2, 3, 4, 5, 6}
TypeError Traceback (most recent call last)
<ipythoninput27ab1b06aa92ec> in <module>()
10 But what if I do the following?
11 """
> 12 s = set([[3,5,6,5],[5,2,1,4,3]])
13 print(s)
Those replace the contents of the set on the left side of the operation
with the result.
For very large sets, this will be more ef cient.
In [29]: # Make a copy of set A
C = [Link]()
# Inplace Set Union ﴾C or B﴿
C |= B
print("Set Union: C \/ B = {}".format(C))
# Make another copy of set A
D = [Link]()
# Inplace Set Intersection ﴾D and B﴿
D &= B
print("Set Intersection: A /\ B = {}".format(D))
# ... similarly for the other operations
# Eventually, the original set A is unchanged
print("Original set A = {}".format(A))
It is a hash table where each element of the hash table (key) points to
another object (value); the object representing the value itself is not
hashed.
Keys and Values are of course Python objects! :)
The easiest way to create one is by using curly braces {} and using
colons to separate keys and values
Associative Array
Source: Wikipedia
In [31]: # Create an empty dictionary
d = {}
# Define a dictionary containing some elements
d = {'a': 1, 'b': 2, 'c': [3, 4]}
# Values can be accessed/added/updated using the same list notation []
# Instead of accessing values by index ﴾int﴿, dictionary's values are accessed by key
# Retrieve the value associated with the key 'b' in the dictionary above
print("Retrieve the value associated with the key 'b' = {}".format(d['b']))
# Add a new value associated with a new key
d['z'] = 'some string'
print("After adding a new entry, the dictionary is: {}".format(d))
# Update the value associated with an existing key
d['a'] = (5, 42)
print("After updating the value of an existing entry, the dictionary is: {}".format(d))
# You can check if a dict contains a key using the same syntax
# as with checking whether a list or tuple contains a value
print("Q: The key 'c' is in the dictionary? A: {}".format('c' in d))
After deleting an existing entry, the dictionary is: {'a': (5, 42), 'c': [3, 4], 'z': 'som
e string'}
After popping out an existing entry, the dictionary is: {'a': (5, 42), 'c': [3, 4]}
The value popped out is: 'some string'
Useful methods: keys and values
The keys and values methods give you iterators of the dictionary’s
keys and values, respectively as sets.
While the key-value pairs are not in any particular order, these functions
output the keys and values in the same order.
In [33]: # Print the set of keys
print("The set of dictionary's keys is: {}".format([Link]()))
# Print the set of values
print("The set of dictionary's values is: {}".format([Link]()))
# One dictionary can be merged into another using the update method ﴾inplace﴿
[Link]({'b' : 'foo', 'c' : 12})
print("After updating, the dictionary is: {}".format(d))
The mapping dictionary is: {'foo': 15, 'bar': 73, 'baz': 42}
Checkpoint Quiz
What happens if the list of keys contains duplicates, i.e., if we change the
de nition of key_list as follows:
NameError Traceback (most recent call last)
<ipythoninput363f280ab9c236> in <module>()
1 # It’s quite common to have logic as follows:
> 2 if key in some_dict:
3 value = some_dict[key]
4 else:
5 value = default_value
Value returned = 0
Value returned = 0
Value returned = None
KeyError Traceback (most recent call last)
<ipythoninput37102ec6e0ed3d> in <module>()
12 print("Value returned = {}".format(value))
13 # ... whilst 'pop' will raise an exception.
> 14 value = [Link]('let')
15 print("Value returned = {}".format(value))
KeyError: 'let'
In [38]: # Another typical situation happens when trying to set values in a dictionary.
# Sometimes those values are other collections, like lists.
# Suppose you want to categorize a list of words by their first letters as a dict of lists.
# List of words
words = ['apple', 'bat', 'bar', 'atom', 'book', 'car', 'charlie', 'zoo']
# Initializing your empty dictionary
index = {}
# Loop through all the words in the list
for word in words:
first_letter = word[0] # extract the first letter from the current word
if first_letter not in index: # if the key ﴾first_letter﴿ is not in the dictionary
index[first_letter] = [word] # just create a new entry, i.e., a list with one word
else:
# otherwise, append the current word to the list associated with the existing key
index[first_letter].append(word)
The index dictionary is: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book'], 'c': ['ca
r', 'charlie'], 'z': ['zoo']}
In [39]: # The ifelse code block above can be easily rewritten using the 'setdefault' dict method.
# List of words
words = ['apple', 'bat', 'bar', 'atom', 'book', 'car', 'charlie', 'zoo']
# Initializing your empty dictionary
index = {}
# Loop through all the words in the list
for word in words:
first_letter = word[0] # extract the first letter from the current word
# either set an empty list ﴾[]﴿ with the current word
# or append it to the existing entry
[Link](first_letter, []).append(word)
The index dictionary is: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book'], 'c': ['ca
r', 'charlie'], 'z': ['zoo']}
Valid Types for Dictionary Keys
Although the values of a dict can be any Python object, the keys have
to be hashable
Therefore keys must be immutable objects like scalar types ( int ,
float , str ) or tuple (note: all the objects in the tuple need to be
immutable, too!).
Again, you can check whether an object is hashable (i.e., can be used as a
key in a dictionary) with the hash() function.
In [40]: # Check if an object of type str is 'hashable'
print(hash('string key'))
# Check if an object of type tuple is 'hashable'
print(hash((1, 2, (2, 3))))
# Check if a composite object of type tuple is 'hashable'
print(hash((1, 2, [2, 3]))) # fails because list are 'unhashable'
8606079362090932906
1097636502276347782
TypeError Traceback (most recent call last)
<ipythoninput408d51b2f45daf> in <module>()
4 print(hash((1, 2, (2, 3))))
5 # Check if a composite object of type tuple is 'hashable'
> 6 print(hash((1, 2, [2, 3]))) # fails because list are 'unhashable'
TypeError Traceback (most recent call last)
<ipythoninput41ab7bcf469625> in <module>()
6 d[tuple([1, 2, 1])] = 'bar'
7 print(d)
> 8 d[tuple([1, 2, [42, 73]])] = 'baz' # fails as the third element of the list is its
elf a list