Python Molecules: Lists, Tuples, and Dictionaries¶
So far, we have talked about some atomic data types: booleans, numbers, and strings. We rarely operate on only one object at a time though. We should be able to store them in some kind of container. This is where our additional data types come in: lists, tuples, and dictionaries.
1 Lists¶
A list is a sequence of objects which can be numbers, booleans, strings … even other lists. Lists are mutable, which means that we can add elements or change them in place.
1.1 Creating lists¶
Creating an empty list is easy, you just need squared brackets.
my_list = []
my_list
[]
By the way, empty lists are evaluated as False
when converted to booleans.
bool([])
False
Again, this might be weird but will come in handy later on. An empty list is of course very boring. So maybe let’s create lists with content.
my_list = [1, 2, 3, 4, 5]
print(my_list)
[1, 2, 3, 4, 5]
my_list = ['A', 'list', 'with', 'strings.']
print(my_list)
['A', 'list', 'with', 'strings.']
Low and behold, a list can contain items of different types.
my_nonhomogenous_list = [1, 'One', True]
print(my_nonhomogenous_list)
[1, 'One', True]
A second way to create lists is using the list
function which converts data types.
my_list = list()
print(my_list)
[]
When you convert a string to a list, it does not become a list with one element, namely the string. Instead, it breaks down the string into characters and stores each as an element.
list_of_characters = list('Wittgenstein')
print(list_of_characters)
['W', 'i', 't', 't', 'g', 'e', 'n', 's', 't', 'e', 'i', 'n']
This example also shows you that elements in a list do not have to be unique.
To add one more level of abstraction, note that lists can also contain other lists.
list_1 = ['a', 'b']
list_2 = ['c', 'd']
my_meta_list = [list_1, list_2]
print(my_meta_list)
[['a', 'b'], ['c', 'd']]
Our meta list contains two elements, list_1
and list_2
, which each contain two elements. Note that this is not the same as having only one list with the four string elements.
1.2 Indexing and slicing lists¶
Indexing and slicing works similar to indexing and slicing for strings which we have encountered before.
my_list = ['a', 'b', 'c', 'd', 'e', 'f']
# first element
my_list[0]
'a'
# first to third element
my_list[0:3]
['a', 'b', 'c']
# every second element
my_list[::2]
['a', 'c', 'e']
# reverse order
my_list[::-1]
['f', 'e', 'd', 'c', 'b', 'a']
Now, with a list of lists this gets a little bit more complicated, we need the squared brackets two times.
list_1 = ['a', 'b']
list_2 = ['c', 'd']
my_meta_list = [list_1, list_2]
my_meta_list[0]
['a', 'b']
With this command, we fetch the first element from my_meta_list
which is also a list consisting of two elements.
my_meta_list[1]
['c', 'd']
my_meta_list[0][1]
'b'
This command let’s us fetch the second element in list_1
which is the first element of my_meta_list
. Do not get confused by the offsets here!
1.3 Add items and combine lists with append
, insert
, and extend
¶
There are several ways to add elements to our lists. If we want to add an item, we can use the append
method. Note that you do not have to reassign the list, since lists are mutable, i.e. they can be changed in place.
my_list = []
my_list.append(1)
my_list.append('Random string')
my_list.append(True)
my_list.append(['Hello', 'World'])
print(my_list)
[1, 'Random string', True, ['Hello', 'World']]
If you want to add an item at a specific place in the list, you can use the insert
method.
my_philosophers = ['Wittgenstein', 'Sen', 'Moore']
my_philosophers.insert(2, 'Russell')
print(my_philosophers)
['Wittgenstein', 'Sen', 'Russell', 'Moore']
If you want to combine two lists, use the extend
method or the +=
operator.
my_philosophers = ['Nozick', 'Sen', 'Rawls']
my_economists = ['Hayek', 'Keynes', 'Friedman']
my_philosophers.extend(my_economists)
print(my_philosophers)
['Nozick', 'Sen', 'Rawls', 'Hayek', 'Keynes', 'Friedman']
my_philosophers = ['Nozick', 'Sen', 'Rawls']
my_economists = ['Hayek', 'Keynes', 'Friedman']
my_economists += my_philosophers
print(my_economists)
['Hayek', 'Keynes', 'Friedman', 'Nozick', 'Sen', 'Rawls']
1.4 Delete items with del
and remove
¶
Again, we have different ways of deleting items of a list. I will only show two. The first deletes the item by offset using the del
statement.
my_economists = ['Acemoglu', 'Johnson', 'Robinson']
del my_economists[1]
my_economists
['Acemoglu', 'Robinson']
Secondly, we can delete an item by value using remove
.
my_economists = ['Acemoglu', 'Johnson', 'Robinson']
my_economists.remove('Johnson')
my_economists
['Acemoglu', 'Robinson']
1.5 Change items¶
Lists are mutable, this means that we can change individual items in place without creating a new list.
my_economists = ['Acemoglu', 'Johnson', 'Robinson']
my_economists[1] = 'Torvik'
print(my_economists)
['Acemoglu', 'Torvik', 'Robinson']
1.6 Some more list methods: index, count, and sort¶
Apart from these basic methods to add, delete and chane items, there exist other useful method which we should briefly look at. The first, index
, gives us the offset of a specific item.
my_economists = ['Hayek', 'Keynes', 'Friedman']
my_economists.index('Keynes')
1
The second one, count
, counts the number of occurences of an item.
my_economists = ['Acemoglu', 'Acemoglu', 'Johnson', 'Robinson']
my_economists.count('Acemoglu')
2
Finally, the sort method can be used to sort a list in place.
my_economists = ['Robinson', 'Johnson', 'Acemoglu']
print(my_economists)
['Robinson', 'Johnson', 'Acemoglu']
my_economists.sort()
print(my_economists)
['Acemoglu', 'Johnson', 'Robinson']
1.7 Testing for membership with in¶
A very useful feature is that we can test whether a certain object is an item of a list.
my_philosophers = ['Kant', 'Mill', 'Wittgenstein']
'Kant' in my_philosophers
True
'Monty Python' in my_philosophers
False
1.8 Caveat: New lists¶
There is one behavior of lists which might puzzle you.
my_economists = ['Hayek', 'Keynes']
my_philosophers = my_economists
my_economists.append('Marx')
print(my_economists)
['Hayek', 'Keynes', 'Marx']
So far, this is probably what you expected. However, how does the list my_philosophers
look like?
print(my_philosophers)
['Hayek', 'Keynes', 'Marx']
It also changed. Instead of creating a copy of the original list and assigning it to the variable my_philosophers
, Python just attaches a second label to the original list objec. So, no matter whether you use the name my_economists
or my_philosopers
, both refer to the same object. To avoid this behavior you have to make explicit copies of the original list. This can be done using either list
command, the copy
method or the complete copy via slicing [:]
.
my_economists = ['Hayek', 'Keynes']
# ways of copying lists
my_philosophers1 = list(my_economists)
my_philosophers2 = my_economists.copy()
my_philosophers3 = my_economists[:]
my_economists.append('Marx')
print(my_philosophers1)
print(my_philosophers2)
print(my_philosophers3)
['Hayek', 'Keynes']
['Hayek', 'Keynes']
['Hayek', 'Keynes']
2 Tuples¶
2.1 Creating a tuple¶
There is another ‘container’ object which is similar to lists, namely tuples. The main difference is that tuples are immutable, so once they are created you cannot change them. They are created by using round brackets. Note that tuples, just as lists, can also be used to store arbitrary objects.
my_tuple = ('1', 1, True)
my_tuple
('1', 1, True)
Actually that is not completely true that you create tuples with round brackets. You can also create tuples without them. If you have only item you just need a trailing comma, if you have more elements, you just have to put commas between each of the items.
not_a_tuple = ('Wittgenstein')
type(not_a_tuple)
str
one_item_tuple = 'Wittgenstein',
type(one_item_tuple)
tuple
another_tuple = 'Acemoglu', 'Johnson', 'Robinson'
type(another_tuple)
tuple
To convert other objects into tuples, you can use the tuple
function.
my_list = ['This', 'is', 'a', 'list', 'of', 'strings']
my_list
['This', 'is', 'a', 'list', 'of', 'strings']
my_tuple = tuple(my_list)
my_tuple
('This', 'is', 'a', 'list', 'of', 'strings')
So, we have tuple, but the strings are not a good description of a list. Maybe change it?
my_tuple[3] = 'tuple'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_2290/2947766814.py in <module>
----> 1 my_tuple[3] = 'tuple'
TypeError: 'tuple' object does not support item assignment
I already said that tuples are immutable, so changing them is not possible.
2.2 Why should I ever use a tuple?¶
Well, if lists can do all that tuples can do and more, why should I ever want to use them? There are several answers. First, precisely because they are immutable, they use less space. Secondly, the fact that you cannot change can also protect you from doing so. Third, tuples will be important later on when we discuss functions, as function arguments are passed as tuples.
Finally, there is a cool thing called tuple unpacking which gives you a very compact way of assigning variables.
my_tuple = ('Kant', 'Mill', 'Aristoteles')
deontology, utilitarianism, virtue = my_tuple
print(utilitarianism)
Mill
Neat, right?
3 Dictionaries¶
OK, after having quickly rushed through tuples we will now talk about the second most important ‘container’ data type after lists. Dictionaries are similar to lists with the key differences being that they are unordered and you cannot retrieve items (or values as they are called here) by their offset but need to use their key. These keys must be unique.
3.1 Creating a dictionary¶
my_dictionary = {'Neoclassical' : 'Marshall', 'NIE' : 'Williamson'}
print(my_dictionary)
{'Neoclassical': 'Marshall', 'NIE': 'Williamson'}
To create a dictionary, you have to use curly brackets. (We could of course also have created an empty dictionary.) Each entry in a dictionary consists of a key and an associated value after the colon. Our dictionary has two keys – the strings ‘Neoclassical’ and ‘NIE’ – as well as two values, the strings ‘Marshall’ and ‘Williamson’. Note that while we have used strings as keys in this example, you can use any of Python’s immutable types.
A second thing you might have noticed is that the order to entries changed when we printed it out. This is what I meant by calling dictionaries unordered.
As always, another way to create a dictionary is to convert another object by using the dict
function. You can do this with:
a list of two-item lists,
a tuple of two-item tuples,
a list of two-item tuples,
a tuple of two-item lists,
a list of two-character strings, and
a tuple of two-character strings.
my_tuple = (('a', 'b'),('c', 'd'))
dict(my_tuple)
{'a': 'b', 'c': 'd'}
my_tuple = ('ab', 'cd')
dict(my_tuple)
{'a': 'b', 'c': 'd'}
What happens here?
my_tuple = ('ab', 'ac')
dict(my_tuple)
{'a': 'c'}
I already said that the keys must be unique. In this case, we would have assigned the strings 'b'
and 'c'
the same key, the string 'a'
. This is not possible.
3.2 Getting an item¶
OK, so … since the elements of a dictionary are unordered how can we get an item? It is very easy, we have to use the key to obtain the corresponding value.
my_philosophers = {'Marx' : 'Karl',
'Wittgenstein' : 'Ludwig',
'Kant' : 'Immanuel'}
print(my_philosophers)
{'Marx': 'Karl', 'Wittgenstein': 'Ludwig', 'Kant': 'Immanuel'}
my_philosophers['Kant']
'Immanuel'
Note however that this will throw you can error if the key does not exist.
my_philosophers['Hayek']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-49-d9e7cdad3670> in <module>()
----> 1 my_philosophers['Hayek']
KeyError: 'Hayek'
We can get around this by using the get
method.
my_philosophers.get('Hayek', 'Not a philosopher.')
'Not a philosopher.'
3.3 Add or change items and combining dictionaries with update¶
Let’s get back to our philosophers.
my_philosophers
{'Kant': 'Immanuel', 'Marx': 'Karl', 'Wittgenstein': 'Ludwig'}
Say we want to add David Hume to our dictionary. Since dictionaries are mutable, we can do this. How you ask?
my_philosophers['Hume'] = 'David'
my_philosophers
{'Hume': 'David', 'Kant': 'Immanuel', 'Marx': 'Karl', 'Wittgenstein': 'Ludwig'}
In the same way, we can also change the value for an existing key.
my_philosophers['Marx'] = 'Carlos'
my_philosophers
{'Hume': 'David',
'Kant': 'Immanuel',
'Marx': 'Carlos',
'Wittgenstein': 'Ludwig'}
That looks just ridiculous. We better change it back.
my_philosophers['Marx'] = 'Karl'
As with lists, we can also combine two dictionaries using the update
method.
my_philosophers = {'Marx' : 'Karl', 'Kant' : 'Immanuel', 'Wittgenstein' : 'Ludwig'}
my_economists = {'Keynes' : 'John', 'Hayek' : 'Friedrich'}
my_philosophers.update(my_economists)
print(my_philosophers)
{'Marx': 'Karl', 'Kant': 'Immanuel', 'Keynes': 'John', 'Wittgenstein': 'Ludwig', 'Hayek': 'Friedrich'}
3.4 Deleting items by keys with del
and all items with clear
¶
As with lists you can delete individual items with the del
statement.
my_philosophers = {'Marx' : 'Karl', 'Kant' : 'Immanuel', 'Wittgenstein' : 'Ludwig'}
print(my_philosophers)
{'Marx': 'Karl', 'Kant': 'Immanuel', 'Wittgenstein': 'Ludwig'}
del my_philosophers['Marx']
print(my_philosophers)
{'Kant': 'Immanuel', 'Wittgenstein': 'Ludwig'}
If you want to clear the whole dictionary, use the clear
method.
my_philosophers.clear()
print(my_philosophers)
{}
3.5 Getting all keys and/or values, with keys
, values
, and items
¶
Sometimes it is helpful to obtain a list (or iterable) of all values or keys of a dictionary. This can be accomplished using the keys and values methods. If you want all key-value pairs in a list of tuples, you need the method items.
my_philosophers = {'Marx' : 'Karl', 'Kant' : 'Immanuel', 'Wittgenstein' : 'Ludwig'}
my_philosophers.keys()
dict_keys(['Marx', 'Kant', 'Wittgenstein'])
list(my_philosophers.keys())
['Marx', 'Kant', 'Wittgenstein']
list(my_philosophers.values())
['Karl', 'Immanuel', 'Ludwig']
list(my_philosophers.items())
[('Marx', 'Karl'), ('Kant', 'Immanuel'), ('Wittgenstein', 'Ludwig')]
3.6 Membership by key with in
¶
Well, testing for membership works similar to lists but you can only do it with keys
my_philosophers = {'Marx' : 'Karl', 'Kant' : 'Immanuel', 'Wittgenstein' : 'Ludwig'}
'Marx' in my_philosophers
True
'Karl' in my_philosophers
False
3.7 The caveat again: new dictionaries¶
Since dictionaries are mutable we have to be careful when we want to create a new dictionary and not only give it a second name. As with lists, this problem can be circumvented by using the copy
method and the dict
function.
my_philosophers = {'Marx' : 'Karl', 'Kant' : 'Immanuel', 'Wittgenstein' : 'Ludwig'}
my_philosophers2 = dict(my_philosophers)
my_philosophers3 = my_philosophers.copy()
del my_philosophers['Marx']
print(my_philosophers2)
print(my_philosophers3)
{'Marx': 'Karl', 'Kant': 'Immanuel', 'Wittgenstein': 'Ludwig'}
{'Marx': 'Karl', 'Kant': 'Immanuel', 'Wittgenstein': 'Ludwig'}
Sources¶
The structure of exposition was taken from Bill Lubanovic (2015): Introducing Python: Modern Computing in Simple Packages. O’Reilly: Sebastopol, CA.