In the previous post, we tackled stuff that has accompanied us since the very first post, that are functions. 👇👇👇
As we already know, many of them are built in Python. But that's not everything, it's just the tip of the iceberg. Now let's find out how to store a collection of elements, or map one element to the other. It is time to dive into tuples, lists, sets, and dictionaries.
Tuples
A tuple is an immutable ordered collection of elements, enclosed in parentheses. Once created, we cannot modify its elements. Tuples are commonly used to group related data together.
As a matter of fact, tuples are something we are already familiar with. Let's dwell on the example from the previous post.
def add_and_subtract_numbers(a, b):
sum_result = a + b
diff_result = a - b
return sum_result, diff_result
sum_result, diff_result = add_and_subtract_numbers(10, 5)
print("Sum:", sum_result)
print("Difference:", diff_result)
In the above example, we have the function that computes two values, outcomes of adding and subtracting input parameters.
def add_and_subtract_numbers(a, b):
sum_result = a + b
diff_result = a - b
return sum_result, diff_result
outcome = add_and_subtract_numbers(10, 5)
print(type(outcome))
Now instead of unpacking returned values, we directly print the type of the returned value.
❯ python3 main.py
<class 'tuple'>
The class of returned value is a tuple!
To define a tuple we can either return multiple values from a function or use round parenthesis ( )
.
>>> x = (1, 'a', 1.1)
>>> print(type(x))
<class 'tuple'>
The one way of extracting values from a tuple is unpacking it.
>>> a, b, c = x
>>> print(a)
1
>>> print(b)
a
>>> print(c)
1.1
As already has been said, if can discard values we are not interested in, using the underscore _
.
>>> _, _, d = x
>>> print(d)
1.1
When a tuple contains many values, we can unpack it partially using an asterisk *
.
>>> x = (1, 'a', 2, 'b', 3, 'c')
>>> a, *y, b = x
>>> print(a)
1
>>> print(y)
['a', 2, 'b', 3]
>>> print(b)
c
>>> type(y)
<class 'list'>
We can easily notice that the first element has been unpacked to a
and the last one to b
, all the rest of the elements have been unpacked to y
as a list. If we are interested in the first and the last elements only, we can simply discard the other ones using the underscore _
.
>>> a, *_, b = x
Another way of extracting values from a tuple is to use square parenthesis []
. This method can be used for slicing a tuple, reversing it, or obtaining concrete elements.
>>> x = (1, 'a', 2, 'b', 3, 'c')
>>> x[2:6]
(2, 'b', 3, 'c')
>>> x[::-1]
('c', 3, 'b', 2, 'a', 1)
>>> x[1]
'a'
>>> x[-1]
'c'
In the above example, we have created another tuple that contains a subset of elements from the original one, we have reversed it and obtained the first and the last element. Please note when slicing or reversing a tuple, the original one remains intact.
We can concatenate tuples using the +
operator.
>>> tuple1 = (1, 2, 3)
>>> tuple2 = (4, 5, 6)
>>> tuple1 + tuple2
(1, 2, 3, 4, 5, 6)
We can repeat elements using the *
operator.
>>> tuple1 * 3
(1, 2, 3, 1, 2, 3, 1, 2, 3)
To check the length of a tuple, we can use the len() function. We can also check if an element exists in a tuple using the membership operator.
>>> colors = ("red", "green", "blue")
>>> len(colors)
3
>>> "red" in colors
True
To convert a tuple to a list and back we can simply use the list() and the tuple() functions respectively.
>>> my_tuple = (10, 20, 30)
>>> tuple_as_list = list(my_tuple)
>>> print(tuple_as_list)
[10, 20, 30]
>>> tuple_as_list.append(40)
>>> modified_tuple = tuple(tuple_as_list)
>>> print(modified_tuple)
(10, 20, 30, 40)
💡 Remember, since tuples are immutable, direct modifications like adding or removing elements are not possible.
Tuples are useful when we have data that should not be changed after creation, like coordinates, constant configurations, or when returning multiple values from a function. Their immutability ensures data integrity and can help prevent unintended modifications.
Lists
A list is a mutable ordered collection of elements, enclosed in square brackets. Compared to tuples, lists can be modified after creation by adding, removing, or changing elements.
To create a list, we can use square parenthesis []
.
>>> aList = []
>>> print(type(aList))
<class 'list'>
>>> print(aList)
[]
Here we have created an empty list. We can create one with prepopulated data. To do that, we simply have to provide elements in brackets.
>>> aList = ['a', 2, 3.3]
>>> print(aList)
['a', 2, 3.3]
We have successfully created a list with three elements: a number, a string, and a float. To access elements we can simply use square brackets []
.
>>> first = aList[0]
>>> second = aList[1]
>>> last = aList[-1]
>>> print(first, second, last)
a 2 3.3
In contrast to tuples, lists are mutable. It means we can easily modify, add, or remove elements during execution.
>>> aList[0] = 1
>>> print(aList)
[1, 2, 3.3]
>>> aList.append(6)
>>> print(aList)
[1, 2, 3.3, 6]
>>> aList.insert(1, 9)
>>> print(aList)
[1, 9, 2, 3.3, 6]
>>> aList.remove(1)
>>> print(aList)
[9, 2, 3.3, 6]
We have overridden the zeroth element to 1. Then we have added 6 to the list. Then 9 has been inserted at position 1. Finally, we have removed element by value.
To remove elements by an index, the pop() method can be used.
>>> aList.pop(2)
3.3
>>> print(aList)
[9, 2, 6]
The float has been removed from the list.
💡 While a function is a block of reusable code that performs a specific task, a method is a function that belongs to an object or a class.
We can repeat elements using the *
operator.
>>> aList = aList * 3
>>> print(aList)
[9, 2, 6, 9, 2, 6, 9, 2, 6]
To create sublists we can use square brackets []
.
>>> aList[2:7]
[6, 9, 2, 6, 9]
To create a copy of the original list we use square brackets with a colon [:]
.
>>> aSecondList = aList[:]
>>> print(aSecondList)
[9, 2, 6, 9, 2, 6, 9, 2, 6]
>>> id(aList)
4681078144
>>> id(aSecondList)
4682931200
To obtain a sorted list we can use the sorted() function.
>>> sorted(aList)
[2, 2, 2, 6, 6, 6, 9, 9, 9]
That function creates another, sorted list from the original one. To sort in-place, we can call the sort() method.
>>> aList.sort()
>>> print(aList)
[2, 2, 2, 6, 6, 6, 9, 9, 9]
To reverse a list, we can use either the reverse() method or the reversed() function.
>>> reversed(aList)
<list_reverseiterator object at 0x11614da20>
>>> list(reversed(aList))
[9, 9, 9, 6, 6, 6, 2, 2, 2]
>>> aList.reverse()
>>> print(aList)
[9, 9, 9, 6, 6, 6, 2, 2, 2]
The reversed() function actually creates an iterator. To obtain a new list, we need to pass the iterator into the list() function.
💡 An iterator is an object that enables the traversal of a collection of elements, one at a time, without needing to know the specific details of the underlying structure of the collection. Iterators provide a uniform way to access and process elements in sequences, containers, or other iterable objects.
There are even more useful methods such as:
list.count()
method provides the ability to count occurrences of an element,list.index()
method provides the ability to find an index of an element,len(list)
function provides a way to find the length of the list,list.clear()
clears all elements from the list.
Lists are incredibly versatile and widely used for managing sequences of items. They offer flexibility through methods for adding, removing, and modifying elements, making them a fundamental tool in programming.
Sets
A set is an unordered collection of unique elements, enclosed in curly braces or created using the set() function. Sets are useful when we need to ensure uniqueness or perform set operations like union, intersection, etc.
To create an empty set we use the set() function, and to create a set with prepopulated data, we use curly braces.
>>> anEmptySet = set()
>>> print(type(anEmptySet))
<class 'set'>
>>> print(anEmptySet)
set()
>>> aSet = {9, 2, 7, 1, 3}
>>> print(type(aSet))
<class 'set'>
>>> print(aSet)
{1, 2, 3, 7, 9}
We can see that even if we have entered the numbers randomly, the set keeps them in order. To add a new element to the set, we simply call add() method.
>>> aSet.add(13)
>>> print(aSet)
{1, 2, 3, 7, 9, 13}
Number 13 has been added to the set. Now to remove an element, we can use either the remove() method or the discard() method.
>>> print(aSet)
{1, 2, 3, 7, 9, 13}
>>> aSet.remove(1)
>>> print(aSet)
{2, 3, 7, 9, 13}
>>> aSet.discard(7)
>>> print(aSet)
{2, 3, 9, 13}
We have removed 1 and 7 from the set. The major difference between those two methods is remove() method throws an exception if an element is not in the set.
>>> print(aSet)
{2, 3, 9, 13}
>>> aSet.discard(1)
>>> aSet.remove(1)
Traceback (most recent call last):
File "<string>", line 1, in <module>
KeyError: 1
We have tried to remove 1 from the set. In the case of the remove() method, a KeyError exception was thrown.
💡 An exception is an event that occurs during the execution of a program, which disrupts the normal flow of the program’s instructions. Exceptions are raised when an error or unexpected condition occurs, and they allow the program to handle these exceptional situations gracefully, rather than crashing.
Sets support a variety of operations, like intersection, union, or difference. Union combines two sets, returning a new set containing all unique elements from both sets.
>>> set1 = {1, 2, 3}
>>> set2 = {3, 4, 5}
>>> set1.union(set2)
{1, 2, 3, 4, 5}
Here we have created a union containing numbers 1, 2, 3, 4, and 5, from the set1
and the set2
. Difference returns a new set containing elements that are in the first set but not in the second set.
>>> set1 = {1, 2, 3}
>>> set2 = {3, 4, 5}
>>> set1.difference(set2)
{1, 2}
Computing the difference between the set1
and the set2
has created a set containing numbers 1 and 2. Intersection returns a new set containing elements that are present in both sets.
>>> set1 = {1, 2, 3}
>>> set2 = {3, 4, 5}
>>> set1.intersection(set2)
{3}
The intersection has created a set containing a single number 3.
We can easily find the number of elements in a set using the len() function.
>>> set1 = {1, 2, 3}
>>> set2 = {3, 4, 5}
>>> len(set1)
3
To find out whether an element is included in the set, we can use membership testing.
>>> set1 = {1, 2, 3}
>>> 1 in set1
True
>>> 5 in set1
False
Sets are particularly useful when we want to work with unique elements and perform set-related operations. They are designed for efficient membership tests and are helpful when we need to ensure that elements are unique within a collection.
Frozensets
A frozenset is an immutable version of a set. It is essentially a set that cannot be modified after it is created. This means we cannot add, remove, or change elements in a frozenset. Frozenset is created using the frozenset() function.
>>> aFrozenSet = frozenset([1, 2, 3, 4])
Frozensets are useful in scenarios where we need to ensure that a set of elements remains constant and should not be modified. They are commonly used as keys in dictionaries when we want to create dictionaries with keys consisting of multiple values.
Dictionaries
A dictionary is a versatile and unordered collection of data in the form of key-value pairs. Each key is unique and is used to access its corresponding value. Dictionaries are also known as associative arrays or hash maps in other programming languages. They are created using curly braces {}
or the dict() constructor.
Let’s create a simple dictionary that maps names to ages. A single key-value pair is provided in the form of 'key': value
and separated from the others by ,
. Everything is embraced by curly braces {}
.
>>> aDict = { 'Adam': 21, 'Paul': 32, 'John': 43 }
>>> print(aDict)
{'Adam': 21, 'Paul': 32, 'John': 43}
We have created a dictionary containing three mappings - three names mapped to corresponding ages.
We can look up values by providing a key in square braces - in our particular example - names.
>>> print(aDict)
{'Adam': 21, 'Paul': 32, 'John': 43}
>>> aDict['Paul']
32
Qyering the dictionary for "Paul" results in obtaining the number 32. To update a mapping, we assign a new value to the existing key.
>>> print(aDict)
{'Adam': 21, 'Paul': 32, 'John': 43}
>>> aDict['Paul'] = 54
>>> print(aDict)
{'Adam': 21, 'Paul': 54, 'John': 43}
Now mapping for "Paul" has been updated to number 54. To insert a new mapping into the dictionary, we assign a new value to the non-existing key.
>>> print(aDict)
{'Adam': 21, 'Paul': 54, 'John': 43}
>>> aDict['Mat'] = 66
>>> print(aDict)
{'Adam': 21, 'Paul': 54, 'John': 43, 'Mat': 66}
A new key-value pair, "Mat": 66, has been added to the dictionary.
Lastly, we can remove elements from a dictionary using the del
keyword.
>>> print(aDict)
{'Adam': 21, 'Paul': 54, 'John': 43, 'Mat': 66}
>>> del aDict['Adam']
>>> print(aDict)
{'Paul': 54, 'John': 43, 'Mat': 66}
In the above example, "Adam" has been removed from the dictionary.
Overall, dictionaries are a fundamental and powerful data structure in Python, widely used in various applications due to their flexibility and efficiency.
Summary
In this post, we explored data structures like tuples, lists, sets and dictionaries. Tuples are ordered, immutable collections of elements and are useful for grouping related data. Lists are ordered, mutable collections of elements. They allow for dynamic changes such as adding, removing, and modifying elements. Sets are unordered collections of unique elements. They ensure the uniqueness of elements and support operations like intersection, union, and difference. Dictionaries are unordered collections of key-value pairs. They enable efficient retrieval of values based on unique keys. Dictionaries are used to represent mappings and associations between data elements.