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
packing
means assigning multiple values to the tuple- syntax:
my_tuple = (value1, value2, value3)
- syntax:
- under the term
unpacking
we 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_tuple
value1
will be the first element in the tuplevalue2
will be a list of all the elements that were not unpackedvalue3
will be the last element in the tuple
- syntax:
- or we cane use it like this:
- syntax:
value1, value2, value3, *value4 = my_tuple
value4
will 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 theunion
method. - We can make an intersection of two sets with the
&
operator. The same can be done with theintersection
method.
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 thedifference
method.
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
issubset
method 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
issuperset
method 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
add
method. - We can remove elements from a set with the
remove
method. - We can clear the set with the
clear
method. - We can delete the set with the
del
keyword. - We can copy the set with the
copy
method. - We can update the set with the
update
method. - We can remove the set with the
pop
method.
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
frozenset
function. - We can convert a list or a tuple to a frozen set with the
frozenset
function. - 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
zip
function to iterate through the two lists.- syntax:
dict(zip(list1, list2))
list1
andlist2
- 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
KeyError
exception. - to avoid this, we can use the
get
method. It will returnNone
if 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
update
method.- 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
in
operator.- 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
del
statement.- syntax:
del my_dict[key]
- syntax:
- or simply
my_dict.pop(key)
- if removing the last element, we can use the
popitem
method -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
clear
method. - we can remove all the items by defining the dictionary as empty
my_dict = {}
. - to delete the dictionary, we can use the
del
keyword. 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
for
loop.- 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.