Lesson 4 - Complex Data Structures in Python - Tuples, Sets and Dictionaries
4.1 Tuples
- Tuples are used to store multiple items in a single variable. They are ordered, immutable/unchangeable collections of elements.
- They allow duplicate elements.
- We generally use tuples when we have a lot of data and we don’t want to change it.
- We write them inside round brackets -
(el1, el2, el3)
1
2
3
4
# tuple example
my_tuple = (1, 2, 3)
print(my_tuple)
print(my_tuple[1]) # access the second element, element on the index 1
1
2
(1, 2, 3)
2
4.1.1 Methods with tuples
- these are the methods we can use with tuples:
count- returns the number of times an element appearsindex- returns the index of an elementlen- returns the length of the tuplemax- returns the maximum valuemin- returns the minimum valuesum- returns the sum of all elementssorted- sorts the tuplereverse- reverses the tupletuple- converts a list to a tuple
1
2
3
4
5
6
7
8
9
10
11
12
# forbidden methods examples
# appending the tuple
my_tuple = (1, 2, 3)
my_tuple.append(4) # AttributeError: 'tuple' object has no attribute 'append'
# changing the elements in the tuple
my_tuple = (1, 2, 3)
my_tuple[1] = 4 # TypeError: 'tuple' object does not support item assignment
# deleting the tuple
del my_tuple # NameError: name 'my_tuple' is not defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[5], line 5
1 # forbidden methods examples
2
3 # appending the tuple
4 my_tuple = (1, 2, 3)
----> 5 my_tuple.append(4) # AttributeError: 'tuple' object has no attribute 'append'
7 # changing the elements in the tuple
8 my_tuple = (1, 2, 3)
AttributeError: 'tuple' object has no attribute 'append'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# allowed methods examples
# len
my_tuple = (1, 2, 3)
print(len(my_tuple))
# max
my_tuple = (1, 2, 3)
print(max(my_tuple))
# min
my_tuple = (1, 2, 3)
print(min(my_tuple))
# sum
my_tuple = (1, 2, 3)
print(sum(my_tuple))
1
2
3
4
3
3
1
6
4.1.2 Tuple Packing and Unpacking
packingmeans assigning multiple values to the tuple- syntax:
my_tuple = (value1, value2, value3)
- syntax:
- under the term
unpackingwe mean extracting the values from the tuple to the variables- syntax:
value1, value2, value3 = my_tuple - in order to do this we need to have the same number of variables as the number of elements in the tuple
- syntax:
1
2
3
4
5
6
7
8
# unpacking the tuple
my_tuple = (1, 2, 3)
print(my_tuple)
a, b, c = my_tuple
print(a)
print(b)
print(c)
1
2
3
4
(1, 2, 3)
1
2
3
- There’s also a special sytax for unpacking a tuple that can be used when the number of variables is not equal to the number of elements in the tuple.
- syntax:
value1, *value2, value3 = my_tuplevalue1will be the first element in the tuplevalue2will be a list of all the elements that were not unpackedvalue3will be the last element in the tuple
- syntax:
- or we cane use it like this:
- syntax:
value1, value2, value3, *value4 = my_tuplevalue4will be a list of all the remaining elements that were not unpacked
- syntax:
1
2
3
4
5
6
7
8
9
10
11
12
13
# example unpacking with asterisk
my_tuple = (1, 2, 3, 4, 5)
print(my_tuple)
a, b, *c = my_tuple
print(a)
print(b)
print(c)
# example unpacking first and last element
first, *middle, last = my_tuple
print(first)
print(middle)
print(last)
1
2
3
4
5
6
7
(1, 2, 3, 4, 5)
1
2
[3, 4, 5]
1
[2, 3, 4]
5
Important! We can use the same unpacking methods with lists and sets.
Tuples can be concatenated with the
+operator, but the result must be a new tuple and if stored, it has to be stored in a new variable, or if given to the same variable, the original tuple will be overwritten.If we wish to change the original tuple, we can convert it to a list and change the elements in the list, after that we can convert it back to a tuple.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# conversion tuple to list, change the list and convert back to tuple
my_tuple = (1, 2, 3)
print(my_tuple)
my_list = list(my_tuple)
my_list[0] = 5
my_tuple = tuple(my_list)
print(my_tuple)
# or
new_tuple = (1, 2, 3)
print(new_tuple)
new_list = list(new_tuple)
new_list.append(4)
new_tuple = tuple(new_list)
print(new_tuple)
1
2
3
4
(1, 2, 3)
(5, 2, 3)
(1, 2, 3)
(1, 2, 3, 4)
4.2 Sets
- Sets are used to store multiple items in a single variable. They are unordered, and do not allow duplicate values.
- A set itself may be modified, but the elements contained in the set must be of an immutable type (lists and dictionaries cannot be inside a set).
- We generally use them when we need to have a collection of unique elements.
- We write them inside curly brackets -
{el1, el2, el3}
1
2
3
# set
my_set = {1, 2, 3, 4, 5}
print(my_set)
1
{1, 2, 3, 4, 5}
- a set can created by converting a list or tuple to a set
1
2
3
4
5
6
# converting to set
my_list = [1,2,3,4,5,6,7,8,9,10]
my_set = set(my_list)
print(my_set)
print(type(my_set))
1
2
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
<class 'set'>
1
2
3
4
5
# if we have more than one equal element in the set, any iteration will be ignored
my_list = [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10]
my_set = set(my_list)
print(my_set)
print(type(my_set))
1
2
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
<class 'set'>
4.2.1 Methods with sets
- We can make a union of two sets with the
|operator. The same can be done withe theunionmethod. - We can make an intersection of two sets with the
&operator. The same can be done with theintersectionmethod.
1
2
3
4
5
# union of sets
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}
print(set1 | set2)
print(set1.union(set2))
1
2
{1, 2, 3, 4, 5, 6, 7, 8}
{1, 2, 3, 4, 5, 6, 7, 8}
1
2
3
4
5
# intersection of sets
a={1,2,3,4,5}
b={4,5,6,7,8}
print(a & b)
print(a.intersection(b))
1
2
{4, 5}
{4, 5}
- We can also make a difference of two sets with the
-operator. The same can be done with thedifferencemethod.
1
2
3
4
5
# difference of sets
a={1,2,3,4,5}
b={4,5,6,7,8}
print(a - b)
print(a.difference(b))
1
2
{1, 2, 3}
{1, 2, 3}
- We can also check if one set is a subset of another with the
issubsetmethod or with<=operator.- If the first set is a subset of the second, the result will be
True, otherwise it will beFalse.
- If the first set is a subset of the second, the result will be
- We can also check if one set is a superset of another with the
issupersetmethod or with>=operator.- If the first set is a superset of the second, the result will be
True, otherwise it will beFalse.
- If the first set is a superset of the second, the result will be
1
2
3
4
5
6
7
# issubset
a={4,5,6}
b={4,5,6,7,8}
print(a.issubset(b))
# issuperset
print(a.issuperset(b))
1
2
True
False
Modification of Sets
- We can add elements to a set with the
addmethod. - We can remove elements from a set with the
removemethod. - We can clear the set with the
clearmethod. - We can delete the set with the
delkeyword. - We can copy the set with the
copymethod. - We can update the set with the
updatemethod. - We can remove the set with the
popmethod.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Create a set
my_set = {1, 2, 3, 4, 5}
# Add elements to a set
my_set.add(6)
print(my_set) # Output: {1, 2, 3, 4, 5, 6}
# Remove elements from a set
my_set.remove(3)
print(my_set) # Output: {1, 2, 4, 5, 6}
# Clear the set
my_set.clear()
print(my_set) # Output: set()
# Delete the set
del my_set
print(my_set) # Output: NameError: name 'my_set' is not defined
# Copy the set
set1 = {1, 2, 3}
set2 = set1.copy()
print(set2) # Output: {1, 2, 3}
# Update the set
set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1.update(set2)
print(set1) # Output: {1, 2, 3, 4, 5}
# Remove an element from the set using pop()
set1 = {1, 2, 3, 4, 5}
set1.pop()
print(set1) # Output: {2, 3, 4, 5}
1
2
3
4
5
6
{1, 2, 3, 4, 5, 6}
{1, 2, 4, 5, 6}
set()
{1, 2, 3}
{1, 2, 3, 4, 5}
{2, 3, 4, 5}
4.2.2 Frozen Sets
- Frozen sets are immutable sets that cannot be modified after creation.
- We can create a frozen set with the
frozensetfunction. - We can convert a list or a tuple to a frozen set with the
frozensetfunction. - all the methods of sets can be used with frozen sets except the ones that modify the set.
1
2
3
4
# frozen set
my_frozen_set = frozenset([1, 2, 3, 4, 5])
print(my_frozen_set)
my_frozen_set.add(6) # AttributeError: 'frozenset' object has no attribute 'add'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
frozenset({1, 2, 3, 4, 5})
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[25], line 4
2 my_frozen_set = frozenset([1, 2, 3, 4, 5])
3 print(my_frozen_set)
----> 4 my_frozen_set.add(6) # AttributeError: 'frozenset' object has no attribute 'add'
AttributeError: 'frozenset' object has no attribute 'add'
4.3 Dictionaries
- dictionary is a collection which is ordered and changeable. It doesn’t allow duplicates.
- dictionaries are written with curly brackets, and they are composed of key-value pairs, that are called items.
- keys are unique within a dictionary, and they can be of any immutable data type such as strings, numbers, or tuples.
- syntax:
my_dict = {key1: value1, key2: value2, key3: value3}
1
2
3
# create a dictionary
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
print(my_dict)
1
{'name': 'John', 'age': 25, 'city': 'New York'}
4.3.1 Methods with dictionaries
- these are the methods we can use with dictionaries:
clear- removes all the elements from the dictionarycopy- returns a copy of the dictionaryfromkeys- returns a new dictionary with the specified keys and valuesget- returns the value of the specified keyitems- returns a list containing the dictionary’s (key, value) tuple pairskeys- returns a list containing the dictionary’s keyspop- removes the element with the specified keypopitem- removes the last inserted itemsetdefault- returns the value of the specified key. If the key does not exist: insert the key, with the specified valueupdate- updates the dictionary with the specified key-value pairsvalues- returns a list of all the values in the dictionarydict- converts a list of tuples into a dictionarylen- returns the number of items in the dictionary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
# # clear - removes all the elements from the dictionary
# my_dict.clear()
# print(my_dict) # Output: {}
# copy - returns a copy of the dictionary
new_dict = my_dict.copy()
print(new_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York'}
# fromkeys - returns a new dictionary with the specified keys and values
keys = ['name', 'age', 'city']
values = ['John', 25, 'New York']
new_dict = dict.fromkeys(keys, values)
print(new_dict) # Output: {'name': ['John', 25, 'New York'], 'age': ['John', 25, 'New York'], 'city': ['John', 25, 'New York']}
# get - returns the value of the specified key
name = my_dict.get('name')
print(name) # Output: John
# items - returns a list containing the dictionary's (key, value) tuple pairs
items = my_dict.items()
print(items) # Output: dict_items([('name', 'John'), ('age', 25), ('city', 'New York')])
# keys - returns a list containing the dictionary's keys
keys = my_dict.keys()
print(keys) # Output: dict_keys(['name', 'age', 'city'])
# pop - removes the element with the specified key
age = my_dict.pop('age')
print(age) # Output: 25
print(my_dict) # Output: {'name': 'John', 'city': 'New York'}
# popitem - removes the last inserted item
item = my_dict.popitem()
print(item) # Output: ('city', 'New York')
print(my_dict) # Output: {'name': 'John'}
# setdefault - returns the value of the specified key. If the key does not exist: insert the key, with the specified value
age = my_dict.setdefault('age', 25)
print(age) # Output: 25
print(my_dict) # Output: {'name': 'John', 'age': 25}
# update - updates the dictionary with the specified key-value pairs
my_dict.update({'city': 'London', 'country': 'UK'})
print(my_dict) # Output: {'name': 'John', 'age': 25, 'city': 'London', 'country': 'UK'}
# values - returns a list of all the values in the dictionary
values = my_dict.values()
print(values) # Output: dict_values(['John', 25, 'London', 'UK'])
# len - returns the length of the dictionary
length = len(my_dict)
print(length) # Output: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{'name': 'John', 'age': 25, 'city': 'New York'}
{'name': ['John', 25, 'New York'], 'age': ['John', 25, 'New York'], 'city': ['John', 25, 'New York']}
John
dict_items([('name', 'John'), ('age', 25), ('city', 'New York')])
dict_keys(['name', 'age', 'city'])
25
{'name': 'John', 'city': 'New York'}
('city', 'New York')
{'name': 'John'}
25
{'name': 'John', 'age': 25}
{'name': 'John', 'age': 25, 'city': 'London', 'country': 'UK'}
dict_values(['John', 25, 'London', 'UK'])
4
4.3.2 Converting lists and tuples into a dictionary
Lists
- There is a special case when we need to convert two lists into a dictionary. We can use the
zipfunction to iterate through the two lists.- syntax:
dict(zip(list1, list2)) list1andlist2- lists that we want to convert into a dictionarydict- converts a list of tuples into a dictionaryzip- returns an iterator of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables
- syntax:
- if the lists have different lengths, the longer list will be truncated.
1
2
3
4
5
6
7
8
9
10
11
# create a dictionary from two lists
keys = ['name', 'age', 'city']
values = ['John', 25, 'New York']
my_dict = dict(zip(keys, values))
print(my_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York'}
# convert two lists of different lengths into a dictionary
keys = ['First Name', 'Last Name', 'Date of Birth', 'Country']
values = ['John', 'Doe', '1990-01-01']
my_dict = dict(zip(keys, values))
print(my_dict) # Output: {'First Name': 'John', 'Last Name': 'Doe', 'Date of Birth': '1990-01-01'}
1
2
{'name': 'John', 'age': 25, 'city': 'New York'}
{'First Name': 'John', 'Last Name': 'Doe', 'Date of Birth': '1990-01-01'}
Tuples
- We could also create a dictionary from a list of tuples.
- syntax:
dict(list_of_tuples) - the tuples in the list must have the same number of elements (key-value pairs).
- syntax:
1
2
3
4
# convert a list of tuples into a dictionary
list_of_tuples = [('name', 'John'), ('age', 25), ('city', 'New York')]
new_dict = dict(list_of_tuples)
print(new_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York'}
1
{'name': 'John', 'age': 25, 'city': 'New York'}
4.3.3 Accessing and modifying the dictionary records
- We can access the dictionary records using the keys.
- syntax:
my_dict[key]
- syntax:
1
2
3
4
# Access elements
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
name = my_dict['name']
print(name) # Output: John
1
John
- if by any chance the key does not exist, we could receive a
KeyErrorexception. - to avoid this, we can use the
getmethod. It will returnNoneif the key does not exist.- syntax:
my_dict.get(key)
- syntax:
- if we want to give a default value when the key doesn’t exist, we can add it to a .get expression.
- syntax:
my_dict.get(key, default_value)
- syntax:
1
2
3
4
5
6
7
8
# get method in a dictionary
my_dict = {'name': 'John', 'age': 25}
graduation = my_dict.get('graduated')
print(graduation) # Output: None
age = my_dict.get('age')
print(age) # Output: 25
city = my_dict.get('city', 'New York')
print(city) # Output: New York, because the key 'city' is not present in the dictionary.
1
2
3
None
25
New York
That gives us a temporary solution that doesn’t change the original dictionary.
- if we want to change the original dictionary, we can use the
updatemethod.- syntax:
my_dict.update({key: value})
- syntax:
- or simply
my_dict[key] = value
1
2
3
4
5
6
7
# update dictionary
my_dict = {'name': 'John', 'age': 25}
my_dict.update({'city': 'New York'})
print(my_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York'}
my_dict['something'] = 'value'
print(my_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York', 'something': 'value'}
1
2
{'name': 'John', 'age': 25, 'city': 'New York'}
{'name': 'John', 'age': 25, 'city': 'New York', 'something': 'value'}
- another way to check if the key exists in the dictionary is to use the
inoperator.- syntax:
key in my_dict
- syntax:
1
2
3
4
# check if key exists
my_dict = {'name': 'John', 'age': 25}
name = 'name' in my_dict
print(name) # Output: True
1
True
4.3.4 Removing elements from the dictionary
- We can remove elements from the dictionary using the
delstatement.- syntax:
del my_dict[key]
- syntax:
- or simply
my_dict.pop(key) - if removing the last element, we can use the
popitemmethod -my_dict.popitem()
1
2
3
4
5
6
7
# example of removing items
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
my_dict.pop('age')
print(my_dict) # Output: {'name': 'John', 'city': 'New York'}
my_dict.popitem()
print(my_dict) # Output: {'name': 'John'}
1
2
{'name': 'John', 'city': 'New York'}
{'name': 'John'}
- to remove all the items, that is to clear the dictionary, we can use the
clearmethod. - we can remove all the items by defining the dictionary as empty
my_dict = {}. - to delete the dictionary, we can use the
delkeyword. Deleting will remove the definition of the variable that contained the dictionary as well.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# to empty a dictionary
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
my_dict.clear()
print(my_dict) # Output: {}
# empty the dictionary
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
my_dict = {}
print(my_dict) # Output: {}
# del method
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
del my_dict
print(my_dict) # NameError: name 'my_dict' is not defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{}
{}
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[13], line 14
12 my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
13 del my_dict
---> 14 print(my_dict) # NameError: name 'my_dict' is not defined
NameError: name 'my_dict' is not defined
4.3.5 Looping/Iterating through the dictionary
- We can loop through the dictionary using the
forloop.- syntax:
for key, value in my_dict.items():- this will give us the key and value of the dictionary
- syntax:
- We can also loop only through the keys:
- syntax:
for key in my_dict.keys():
- syntax:
- or through the values:
- syntax:
for value in my_dict.values():
- syntax:
1
2
3
4
5
6
7
8
9
10
11
12
# loop through dictionary
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
for key, value in my_dict.items():
print(key, value)
# loop through the keys
for key in my_dict.keys():
print(key)
# loop through the values
for value in my_dict.values():
print(value)
1
2
3
4
5
6
7
8
9
name John
age 25
city New York
name
age
city
John
25
New York
Nesting dictionaries and lists
- We can nest dictionaries and lists in dictionaries and lists.
- there is no limit to the nesting level.
- only limitation we could have is that the keys have to be unique and the key has to be a strings, numbers or tuples.
- syntax:
my_dict = {'name': 'John', 'age': 25, 'city': 'New York', 'address': {'street': 'Main Street', 'number': 123}}
In order to print the nested dictionary, we can use the pprint module. It will print the dictionary in a more readable format.
- syntax:
1 2
import pprint pprint.pprint(my_dict)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pprint
# nested dictionaries
my_dict = {'name': 'John', 'age': 25, 'city': 'New York'}
nested_dict = {'name': 'Jane', 'age': 30, 'city': 'London'}
my_dict.update({'friend': nested_dict})
# print dictionary with normal print function
print(my_dict) # Output: {'name': 'John', 'age': 25, 'city': 'New York', 'friend': {'name': 'Jane', 'age': 30, 'city': 'London'}}
# print dictionary with pprint function
pprint.pprint(my_dict)
# access nested dictionary
friend = my_dict['friend']
print(friend) # Output: {'name': 'Jane', 'age': 30, 'city': 'London'}
# print friend's city
friend_city = my_dict['friend']['city']
print(friend_city) # Output: London
1
2
3
4
5
6
7
{'name': 'John', 'age': 25, 'city': 'New York', 'friend': {'name': 'Jane', 'age': 30, 'city': 'London'}}
{'age': 25,
'city': 'New York',
'friend': {'age': 30, 'city': 'London', 'name': 'Jane'},
'name': 'John'}
{'name': 'Jane', 'age': 30, 'city': 'London'}
London
- We can have dictionaries nested in lists.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pprint
# list of students
students = [
{'name': 'John', 'age': 25, 'city': 'New York'},
{'name': 'Jane', 'age': 30, 'city': 'London'},
{'name': 'Bob', 'age': 35, 'city': 'Paris'}
]
pprint.pprint(students) # Output: [{'name': 'John', 'age': 25, 'city': 'New York'}, {'name': 'Jane', 'age': 30, 'city': 'London'}, {'name': 'Bob', 'age': 35, 'city': 'Paris'}]
# access student 2
student2 = students[1]
print(student2) # Output: {'name': 'Jane', 'age': 30, 'city': 'London'}
# loop throught students and print their names
for student in students:
name = student['name']
print(name) # Output: John, Jane, Bob
1
2
3
4
5
6
7
[{'age': 25, 'city': 'New York', 'name': 'John'},
{'age': 30, 'city': 'London', 'name': 'Jane'},
{'age': 35, 'city': 'Paris', 'name': 'Bob'}]
{'name': 'Jane', 'age': 30, 'city': 'London'}
John
Jane
Bob
Important! These combinations of the dictionary and list are very common. This is the cornerstone of the JSON format.


