Python Basics You Need to Know, Always: Data Structures | by Shubhangi Hora | Aug, 2022
A data structure is a format of storing, organizing, and handling data that allows you to perform operations on it efficiently
For example, storing people’s email addresses, recording the temperature every hour throughout the day, etc. Data in these structures can be referred to as elements.
There are many data structures and almost all of them can be implemented in any programming language — either they are built into the language or they can be created or imported.
In this article, we’re going to explore data structures using Python.
So, there are 4 data structures that are built directly into python — list, dictionary, set and tuple — that we’ll be talking about in this article. One quick thing to understand before we get into each of these structures is the concept of mutability — the ability to change. Some data structures are mutable, i.e. the structure can be modified — elements can be added, removed and updated post creation of the data structure. Other data structures are immutable, i.e. no modifications can be made to them after they have been created.
List
A list stores data as comma separated elements within square brackets. It is an ordered, sequential, heterogeneous, indexed, mutable, non-unique data structure.
odd = [1, 3, 5, 7, 9]
Ordered: a list maintains the order of elements as you insert them.
Sequential: a list can be iterated over.
Heterogeneous: The elements stored within a list can be of different data types, so you can have a list that contains an integer, then a string, then a float. Due to this feature, data is less tightly packed together (memory wise) and hence lists take up more space.
Indexed: Every element in a list has an index associated with it, which is basically like an address. The index is used to access the element.
Positive indexing starts at 0 for the first element in the list and continues to n-1 for the last element, where n is the number of elements. This is known as forward traversal.
Negative indexing starts at -1 for the last element in the list and continues to -n for the first element, where n is the number of elements. This is known as reverse traversal.
Mutable: Lists are implemented as dynamic arrays behind the scenes in python, hence they are mutable. You can add, remove and modify elements in a list and python will automatically release or allocate memory accordingly. Some operations such as adding or removing an element at the start of the list are expensive since the index of every other element in the list needs to be changed.
Non-unique: the same element can be stored twice within a list, i.e. lists can contain duplicate values.
Let’s code!
Create a list — you can do using square brackets [] or by calling the list() method.
ids = [234, 534, 239, 392]letters = list(‘shubhangi’)
#creating a list of a string breaks the string into individual #characters
>>> ['s', 'h', 'u', 'b', 'h', 'a', 'n', 'g', 'i']names = list([‘shubhangi’, ‘nita’, ‘pooja’, ‘jordan’])
>>> ['shubhangi', 'nita', 'pooja', 'jordan']mixed = [234, ‘shubhangi’, ‘nita’, 23.9]
Add an element to the end of a list
names.append(‘shagun’)
>>>['shubhangi', 'nita', 'jordan', 'shagun']names.append(‘nita’)
>>>['shubhangi', 'nita', 'pooja', 'jordan', 'shagun', 'nita']
Add element to specific index of a list
names.insert(2, ‘nidhi’)
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’, ‘nita’]
Count number of occurrences of the same value
names.count(‘nita’)
>>>2
Remove element with specific value from the list. If the element of that value occurs more than once, then only the first occurence is removed.
names.remove(‘pooja’)
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘jordan’, ‘shagun’, ‘nita’]names.remove(‘nita’)
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’, ‘shagun’, ‘nita’]
Remove element at a specific index
names.pop(3)
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’, ‘nita’]
Remove last element from list
names.pop()
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’]
Fetch index of element with specific value
names.index(‘jordan’)
>>>2
Reverse the list
names.reverse()
>>>[‘jordan’, ‘nidhi’, ‘shubhangi’]
List comprehensions: this is my favorite part about lists. You can create, add to, modify and basically do anything to a list in the form of a comprehension, which is a much more elegant and comprehensive way to convert multiple lines of code into one!
Let’s say you want a list of the squares of numbers ranging from 1 to 40. A tedious way to go about it is
squares = []
for x in range (1, 41):
squares.append(x**2)>>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600]
With list comprehensions you can simply write
squares = [x**2 for x in range (1,41)]
>>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600]
You can also add an if condition to a list comprehension! Let’s create a list from the ‘squares’ list consisting of numbers that are divisible by 2
nums = [num for num in squares if num%2==0]
>>>[4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576, 676, 784, 900, 1024, 1156, 1296, 1444, 1600]
Let’s add an else too!
result = [num if num%2==0 else ‘no’ for num in squares]
>>>[‘no’, 4, ‘no’, 16, ‘no’, 36, ‘no’, 64, ‘no’, 100, ‘no’, 144, ‘no’, 196, ‘no’, 256, ‘no’, 324, ‘no’, 400, ‘no’, 484, ‘no’, 576, ‘no’, 676, ‘no’, 784, ‘no’, 900, ‘no’, 1024, ‘no’, 1156, ‘no’, 1296, ‘no’, 1444, ‘no’, 1600]
Note: when there’s just an if condition then the if comes after the for. when there’s an if-else condition then the if and else come before the for.
Slicing: this is another cool feature of lists! It’s a method that uses indexes to extract subsets from a list. The basic syntax is
<nameOfList>[<startingIndex>:<endIndex>:<indexJump>]
names = [‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’]
Fetch the whole list
names[::]
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’]
Fetch elements between two indexes
names[1:5]
>>>[‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’]
#(elements with index 1, 2, 3, 4)
Fetch alternate elements from the list
names[::2]
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’]
#(starts at index 0, skips the next index, returns the next element, # and so on)
Fetch alternate elements between two indexes
names[1:6:2]
>>>[‘nita’, ‘pooja’, ‘shagun’]
Reverse the list
names[::-1]
>>>[‘shagun’, ‘jordan’, ‘pooja’, ‘nidhi’, ‘nita’, ‘shubhangi’]
Tuple
A tuple stores data as comma separated elements within parentheses. It is immutable, heterogeneous, ordered, sequential and indexed.
odd = (1, 3, 5, 7,9)
Immutable: a tuple itself is immutable which means elements cannot be added, removed or modified dynamically. Once a tuple has been initialised, it cannot be changed. This is why they are more memory efficient than lists because memory is fixed at the time of creation itself. This also makes the lookup activity faster.
Heterogeneous: like lists, tuples can hold elements of different data types, including different data structures. For example, a tuple could have a string, followed by a float, followed by a list. Hence, despite a tuple being immutable it can hold mutable objects. Due to this, data is less tightly packed together (just like in lists).
Ordered: like lists, tuples retain the order of elements. The order will always be the same as you created it to be.
Sequential: tuples are iterable exactly like lists.
Indexed: tuples are indexed in the exact same manner as lists and hence elements can be accessed in the same way too.
So basically, a tuple is a list that is immutable.
Let’s code!
Creating a tuple
data = (1, 3, ‘strawberry’, [9, 3, 4])even_nums = 2, 4, 6, 8
>>> (2, 4, 6, 8)type(even_nums)
>>> <class ‘tuple’>nums = even_nums, (1, 3, 5, 7) # nested tuples
>>> ((2, 4, 6, 8), (1, 3, 5, 7))letters = tuple([‘a’, ‘b’, ‘c’])
>>> (‘a’, ‘b’, ‘c’)# initialise a tuple with only one element. if done without the
# comma it won’t create a tuple
single = 1,
>>> (1,)
single_test = (1)
type(single_test)
>>> <class 'int'>
Unpacking a tuple (storing its values into individual variables)
a, b, c, d = even_nums
print(a, b, c, d)
>>> 2 4 6 8
Other methods are the same as lists, except no insertion, deletion or updation of elements.
Dictionary
A dictionary stores data in the form of key-value pairs. It is a sequential, heterogeneous, unordered, indexed, mutable data structure.
numbers = {'odd':[1, 3, 5, 7, 9], 'even':[2, 4, 6, 8]}
Pretty much like a phone address book where for a person’s name you have a phone number. The name is the key and the number is the value. A key can have only one value. The value itself can be another data structure that can hold multiple elements, like a list. A dictionary is also known as a map, hash map, associative array and lookup table.
Sequential: like a list and a tuple, a dictionary can be iterated over.
Heterogeneous: Like a list, a dictionary can hold keys and values of different data types.
Unordered: python 3.6.2 onwards in built dictionaries are ordered; they retain the order in which the keys were inserted. Prior to this though, they don’t retain the order.
Indexed: The gist of it is simply that you can fetch elements based on their keys. These keys are what index the dictionary and they can be of any hashable type — a hashable object is one whose value does not change during its lifetime, hence any object that is immutable is hashable, such as numbers and strings.
Mutable: While the keys in a dictionary are immutable, the dictionary itself is mutable. This means that you can add, delete and update key-value pairs post creation. Since a dictionary is mutable, it doesn’t have a fixed size at the time of creation — it is dynamic.
Dictionaries are also pretty efficient when it comes to carrying out operations — they have a time complexity of O(1) for lookup, insertion, updation and deletion.
While this dictionary is directly available in Python, there are versions of it with different features that can be imported from the collections library and the types library. Some of these versions are as follows:
- Ordered dictionary: this retains the order of key insertion and has some methods that the basic dictionary doesn’t (python 3.6+ normal dictionaries also preserve insertion order)
- Default dictionary: this returns a default value / response if you try to fetch the value of a key that doesn’t exist yet
- Chain map: this groups multiple dictionaries into one mapping; when you perform a lookup on a mapping, every individual dictionary within the mapping is searched until the key is found
- Counter: this counts hashable objects, i.e. it counts the number of times an element has appeared and stores that as a value with the respective element as the key
- Mapping proxy type: this is a wrapper that creates a read only, i.e. immutable, proxy dictionary based off your original mutable dictionary
Let’s code!
Create a dictionary
d = {‘a’:’apple’, ‘b’:’banana’} # using {}d = dict(a=’apple’, b=’banana’) # using dict() methodd = dict([(‘a’,’apple’), (‘b’,’banana’)]) # using dict method on a list of tuplesids = [234, 534, 239, 392]
names = ['shubhangi', 'nita', 'pooja', 'jordan']
students = dict(zip(ids, names))
>>>{234: 'shubhangi', 534: 'nita', 239: 'pooja', 392: 'jordan'}
# using the zip method on two separate lists
Fetch keys of the dictionary
students.keys()
>>>dict_keys([234, 534, 239, 392])list(students) # returns keys as a list
>>>[234, 534, 239, 392]sorted(students) # returns keys in a sorted order
>>>[234, 239, 392, 534]
Fetch values of the dictionary
students.values()
>>>dict_values(['shubhangi', 'nita', 'pooja', 'jordan'])
Fetch value for particular key
# time efficient but can raise KeyError if key doesn't exist
students.get(239)
>>>'pooja'# return default value if key is not present
students.get(123, 'not present')
>>>'not present'students[239]
>>>'pooja'
Delete a particular key value pair
students.pop(239)
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan'}del students[239]
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan'}
Check if a particular key exists / does not exist in the dictionary
234 in students
>>>True123 not in students
>>>True
Check if dictionary is empty
if students:
...print("not empty")
else:
...print("emptyy")
>>>not empty
Iterate over key value pairs
for k, v in students.items():
...print(k, v)
>>>234 shubhangi
534 nita
392 jordan
Add key-value pair to a dictionary
students.update({123:'nidhi'})
>>> {234: 'shubhangi', 534: 'nita', 392: 'jordan', 123: 'nidhi'}students[901] = 'radhika'
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan', 123: 'nidhi', 901: 'radhika'}
Dictionary comprehensions: just like lists, dictionaries can also be made and modified using comprehensions!
nums = { k: k**2 for k in (1, 2, 3)}
>>> {1: 1, 2: 4, 3: 9}nums.update((k, k**3) for k,v in nums.items())
>>>{1: 1, 2: 8, 3: 27}nums.update((k, v**3) for k,v in nums.items())
>>>{1: 1, 2: 64, 3: 729}
Empty a dictionary
nums.clear()
>>>{}
Sets
A set is a collection of comma separated, unordered, unique elements within curly brackets. It is mutable, unindexed and heterogeneous.
vowels = {'a', 'e', 'i', 'o', 'u'}
Unordered: the order of elements is not maintained.
Unique: unlike lists, sets do not store duplicate values.
Unindexed: since the elements within a set are unordered, there is no index for them.
Mutable: like lists, sets are mutable. Hence elements can be added and removed post creation. Elements cannot be altered, however, as there is no way to access specific elements due to lack of indexing.
Heterogeneous: sets can hold elements of different data types as long as they are hashable, i.e. the elements themselves should be immutable. So a set cannot have a list within it, for example.
Sets allow operations such as the sets in mathematics (the ones with Venn diagrams) — intersection, union, difference, etc. All these operations have a time complexity of O(n).
Sets are backed by the dictionary data type in python and hence have a lot of similar characteristics.
There is also a type of set that is immutable, known as a frozen set. This can be used as the keys in a dictionary (since those are immutable) or as elements of another set.
Let’s code!
Creating a set
vowels = set(‘aeiou’)
>>> {‘u’, ‘e’, ‘o’, ‘i’, ‘a’}myname = set(‘shubhangi’)
>>> {‘u’, ’n’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Like lists and dictionaries, sets also work with comprehensions!
even = {x for x in range (20) if x%2==0}
>>> {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
Fetch the intersection of two sets, i.e. the common elements in two sets
myname.intersection(vowels)
>>> {‘u’, ‘i’, ‘a’}
Join two sets together (fetch their union)
myname.union(vowels)
>>> {‘u’, ’n’, ‘e’, ‘o’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Fetch the elements that are present in one set and absent in another
myname.difference(vowels)
>>> {’n’, ‘b’, ‘h’, ‘s’, ‘g’}
Add an element to the set
myname.add(‘hora’)
>>> {‘u’, ’n’, ‘hora’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Remove an element from the set
myname.remove(‘hora’)
>>> {‘u’, ’n’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
A data structure is a format of storing, organizing, and handling data that allows you to perform operations on it efficiently
For example, storing people’s email addresses, recording the temperature every hour throughout the day, etc. Data in these structures can be referred to as elements.
There are many data structures and almost all of them can be implemented in any programming language — either they are built into the language or they can be created or imported.
In this article, we’re going to explore data structures using Python.
So, there are 4 data structures that are built directly into python — list, dictionary, set and tuple — that we’ll be talking about in this article. One quick thing to understand before we get into each of these structures is the concept of mutability — the ability to change. Some data structures are mutable, i.e. the structure can be modified — elements can be added, removed and updated post creation of the data structure. Other data structures are immutable, i.e. no modifications can be made to them after they have been created.
List
A list stores data as comma separated elements within square brackets. It is an ordered, sequential, heterogeneous, indexed, mutable, non-unique data structure.
odd = [1, 3, 5, 7, 9]
Ordered: a list maintains the order of elements as you insert them.
Sequential: a list can be iterated over.
Heterogeneous: The elements stored within a list can be of different data types, so you can have a list that contains an integer, then a string, then a float. Due to this feature, data is less tightly packed together (memory wise) and hence lists take up more space.
Indexed: Every element in a list has an index associated with it, which is basically like an address. The index is used to access the element.
Positive indexing starts at 0 for the first element in the list and continues to n-1 for the last element, where n is the number of elements. This is known as forward traversal.
Negative indexing starts at -1 for the last element in the list and continues to -n for the first element, where n is the number of elements. This is known as reverse traversal.
Mutable: Lists are implemented as dynamic arrays behind the scenes in python, hence they are mutable. You can add, remove and modify elements in a list and python will automatically release or allocate memory accordingly. Some operations such as adding or removing an element at the start of the list are expensive since the index of every other element in the list needs to be changed.
Non-unique: the same element can be stored twice within a list, i.e. lists can contain duplicate values.
Let’s code!
Create a list — you can do using square brackets [] or by calling the list() method.
ids = [234, 534, 239, 392]letters = list(‘shubhangi’)
#creating a list of a string breaks the string into individual #characters
>>> ['s', 'h', 'u', 'b', 'h', 'a', 'n', 'g', 'i']names = list([‘shubhangi’, ‘nita’, ‘pooja’, ‘jordan’])
>>> ['shubhangi', 'nita', 'pooja', 'jordan']mixed = [234, ‘shubhangi’, ‘nita’, 23.9]
Add an element to the end of a list
names.append(‘shagun’)
>>>['shubhangi', 'nita', 'jordan', 'shagun']names.append(‘nita’)
>>>['shubhangi', 'nita', 'pooja', 'jordan', 'shagun', 'nita']
Add element to specific index of a list
names.insert(2, ‘nidhi’)
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’, ‘nita’]
Count number of occurrences of the same value
names.count(‘nita’)
>>>2
Remove element with specific value from the list. If the element of that value occurs more than once, then only the first occurence is removed.
names.remove(‘pooja’)
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘jordan’, ‘shagun’, ‘nita’]names.remove(‘nita’)
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’, ‘shagun’, ‘nita’]
Remove element at a specific index
names.pop(3)
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’, ‘nita’]
Remove last element from list
names.pop()
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’]
Fetch index of element with specific value
names.index(‘jordan’)
>>>2
Reverse the list
names.reverse()
>>>[‘jordan’, ‘nidhi’, ‘shubhangi’]
List comprehensions: this is my favorite part about lists. You can create, add to, modify and basically do anything to a list in the form of a comprehension, which is a much more elegant and comprehensive way to convert multiple lines of code into one!
Let’s say you want a list of the squares of numbers ranging from 1 to 40. A tedious way to go about it is
squares = []
for x in range (1, 41):
squares.append(x**2)>>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600]
With list comprehensions you can simply write
squares = [x**2 for x in range (1,41)]
>>>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600]
You can also add an if condition to a list comprehension! Let’s create a list from the ‘squares’ list consisting of numbers that are divisible by 2
nums = [num for num in squares if num%2==0]
>>>[4, 16, 36, 64, 100, 144, 196, 256, 324, 400, 484, 576, 676, 784, 900, 1024, 1156, 1296, 1444, 1600]
Let’s add an else too!
result = [num if num%2==0 else ‘no’ for num in squares]
>>>[‘no’, 4, ‘no’, 16, ‘no’, 36, ‘no’, 64, ‘no’, 100, ‘no’, 144, ‘no’, 196, ‘no’, 256, ‘no’, 324, ‘no’, 400, ‘no’, 484, ‘no’, 576, ‘no’, 676, ‘no’, 784, ‘no’, 900, ‘no’, 1024, ‘no’, 1156, ‘no’, 1296, ‘no’, 1444, ‘no’, 1600]
Note: when there’s just an if condition then the if comes after the for. when there’s an if-else condition then the if and else come before the for.
Slicing: this is another cool feature of lists! It’s a method that uses indexes to extract subsets from a list. The basic syntax is
<nameOfList>[<startingIndex>:<endIndex>:<indexJump>]
names = [‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’]
Fetch the whole list
names[::]
>>>[‘shubhangi’, ‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’, ‘shagun’]
Fetch elements between two indexes
names[1:5]
>>>[‘nita’, ‘nidhi’, ‘pooja’, ‘jordan’]
#(elements with index 1, 2, 3, 4)
Fetch alternate elements from the list
names[::2]
>>>[‘shubhangi’, ‘nidhi’, ‘jordan’]
#(starts at index 0, skips the next index, returns the next element, # and so on)
Fetch alternate elements between two indexes
names[1:6:2]
>>>[‘nita’, ‘pooja’, ‘shagun’]
Reverse the list
names[::-1]
>>>[‘shagun’, ‘jordan’, ‘pooja’, ‘nidhi’, ‘nita’, ‘shubhangi’]
Tuple
A tuple stores data as comma separated elements within parentheses. It is immutable, heterogeneous, ordered, sequential and indexed.
odd = (1, 3, 5, 7,9)
Immutable: a tuple itself is immutable which means elements cannot be added, removed or modified dynamically. Once a tuple has been initialised, it cannot be changed. This is why they are more memory efficient than lists because memory is fixed at the time of creation itself. This also makes the lookup activity faster.
Heterogeneous: like lists, tuples can hold elements of different data types, including different data structures. For example, a tuple could have a string, followed by a float, followed by a list. Hence, despite a tuple being immutable it can hold mutable objects. Due to this, data is less tightly packed together (just like in lists).
Ordered: like lists, tuples retain the order of elements. The order will always be the same as you created it to be.
Sequential: tuples are iterable exactly like lists.
Indexed: tuples are indexed in the exact same manner as lists and hence elements can be accessed in the same way too.
So basically, a tuple is a list that is immutable.
Let’s code!
Creating a tuple
data = (1, 3, ‘strawberry’, [9, 3, 4])even_nums = 2, 4, 6, 8
>>> (2, 4, 6, 8)type(even_nums)
>>> <class ‘tuple’>nums = even_nums, (1, 3, 5, 7) # nested tuples
>>> ((2, 4, 6, 8), (1, 3, 5, 7))letters = tuple([‘a’, ‘b’, ‘c’])
>>> (‘a’, ‘b’, ‘c’)# initialise a tuple with only one element. if done without the
# comma it won’t create a tuple
single = 1,
>>> (1,)
single_test = (1)
type(single_test)
>>> <class 'int'>
Unpacking a tuple (storing its values into individual variables)
a, b, c, d = even_nums
print(a, b, c, d)
>>> 2 4 6 8
Other methods are the same as lists, except no insertion, deletion or updation of elements.
Dictionary
A dictionary stores data in the form of key-value pairs. It is a sequential, heterogeneous, unordered, indexed, mutable data structure.
numbers = {'odd':[1, 3, 5, 7, 9], 'even':[2, 4, 6, 8]}
Pretty much like a phone address book where for a person’s name you have a phone number. The name is the key and the number is the value. A key can have only one value. The value itself can be another data structure that can hold multiple elements, like a list. A dictionary is also known as a map, hash map, associative array and lookup table.
Sequential: like a list and a tuple, a dictionary can be iterated over.
Heterogeneous: Like a list, a dictionary can hold keys and values of different data types.
Unordered: python 3.6.2 onwards in built dictionaries are ordered; they retain the order in which the keys were inserted. Prior to this though, they don’t retain the order.
Indexed: The gist of it is simply that you can fetch elements based on their keys. These keys are what index the dictionary and they can be of any hashable type — a hashable object is one whose value does not change during its lifetime, hence any object that is immutable is hashable, such as numbers and strings.
Mutable: While the keys in a dictionary are immutable, the dictionary itself is mutable. This means that you can add, delete and update key-value pairs post creation. Since a dictionary is mutable, it doesn’t have a fixed size at the time of creation — it is dynamic.
Dictionaries are also pretty efficient when it comes to carrying out operations — they have a time complexity of O(1) for lookup, insertion, updation and deletion.
While this dictionary is directly available in Python, there are versions of it with different features that can be imported from the collections library and the types library. Some of these versions are as follows:
- Ordered dictionary: this retains the order of key insertion and has some methods that the basic dictionary doesn’t (python 3.6+ normal dictionaries also preserve insertion order)
- Default dictionary: this returns a default value / response if you try to fetch the value of a key that doesn’t exist yet
- Chain map: this groups multiple dictionaries into one mapping; when you perform a lookup on a mapping, every individual dictionary within the mapping is searched until the key is found
- Counter: this counts hashable objects, i.e. it counts the number of times an element has appeared and stores that as a value with the respective element as the key
- Mapping proxy type: this is a wrapper that creates a read only, i.e. immutable, proxy dictionary based off your original mutable dictionary
Let’s code!
Create a dictionary
d = {‘a’:’apple’, ‘b’:’banana’} # using {}d = dict(a=’apple’, b=’banana’) # using dict() methodd = dict([(‘a’,’apple’), (‘b’,’banana’)]) # using dict method on a list of tuplesids = [234, 534, 239, 392]
names = ['shubhangi', 'nita', 'pooja', 'jordan']
students = dict(zip(ids, names))
>>>{234: 'shubhangi', 534: 'nita', 239: 'pooja', 392: 'jordan'}
# using the zip method on two separate lists
Fetch keys of the dictionary
students.keys()
>>>dict_keys([234, 534, 239, 392])list(students) # returns keys as a list
>>>[234, 534, 239, 392]sorted(students) # returns keys in a sorted order
>>>[234, 239, 392, 534]
Fetch values of the dictionary
students.values()
>>>dict_values(['shubhangi', 'nita', 'pooja', 'jordan'])
Fetch value for particular key
# time efficient but can raise KeyError if key doesn't exist
students.get(239)
>>>'pooja'# return default value if key is not present
students.get(123, 'not present')
>>>'not present'students[239]
>>>'pooja'
Delete a particular key value pair
students.pop(239)
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan'}del students[239]
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan'}
Check if a particular key exists / does not exist in the dictionary
234 in students
>>>True123 not in students
>>>True
Check if dictionary is empty
if students:
...print("not empty")
else:
...print("emptyy")
>>>not empty
Iterate over key value pairs
for k, v in students.items():
...print(k, v)
>>>234 shubhangi
534 nita
392 jordan
Add key-value pair to a dictionary
students.update({123:'nidhi'})
>>> {234: 'shubhangi', 534: 'nita', 392: 'jordan', 123: 'nidhi'}students[901] = 'radhika'
>>>{234: 'shubhangi', 534: 'nita', 392: 'jordan', 123: 'nidhi', 901: 'radhika'}
Dictionary comprehensions: just like lists, dictionaries can also be made and modified using comprehensions!
nums = { k: k**2 for k in (1, 2, 3)}
>>> {1: 1, 2: 4, 3: 9}nums.update((k, k**3) for k,v in nums.items())
>>>{1: 1, 2: 8, 3: 27}nums.update((k, v**3) for k,v in nums.items())
>>>{1: 1, 2: 64, 3: 729}
Empty a dictionary
nums.clear()
>>>{}
Sets
A set is a collection of comma separated, unordered, unique elements within curly brackets. It is mutable, unindexed and heterogeneous.
vowels = {'a', 'e', 'i', 'o', 'u'}
Unordered: the order of elements is not maintained.
Unique: unlike lists, sets do not store duplicate values.
Unindexed: since the elements within a set are unordered, there is no index for them.
Mutable: like lists, sets are mutable. Hence elements can be added and removed post creation. Elements cannot be altered, however, as there is no way to access specific elements due to lack of indexing.
Heterogeneous: sets can hold elements of different data types as long as they are hashable, i.e. the elements themselves should be immutable. So a set cannot have a list within it, for example.
Sets allow operations such as the sets in mathematics (the ones with Venn diagrams) — intersection, union, difference, etc. All these operations have a time complexity of O(n).
Sets are backed by the dictionary data type in python and hence have a lot of similar characteristics.
There is also a type of set that is immutable, known as a frozen set. This can be used as the keys in a dictionary (since those are immutable) or as elements of another set.
Let’s code!
Creating a set
vowels = set(‘aeiou’)
>>> {‘u’, ‘e’, ‘o’, ‘i’, ‘a’}myname = set(‘shubhangi’)
>>> {‘u’, ’n’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Like lists and dictionaries, sets also work with comprehensions!
even = {x for x in range (20) if x%2==0}
>>> {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
Fetch the intersection of two sets, i.e. the common elements in two sets
myname.intersection(vowels)
>>> {‘u’, ‘i’, ‘a’}
Join two sets together (fetch their union)
myname.union(vowels)
>>> {‘u’, ’n’, ‘e’, ‘o’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Fetch the elements that are present in one set and absent in another
myname.difference(vowels)
>>> {’n’, ‘b’, ‘h’, ‘s’, ‘g’}
Add an element to the set
myname.add(‘hora’)
>>> {‘u’, ’n’, ‘hora’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}
Remove an element from the set
myname.remove(‘hora’)
>>> {‘u’, ’n’, ‘b’, ‘h’, ‘s’, ‘g’, ‘i’, ‘a’}