Python Containers

Containers are mostly common used in not only python but also other programming languages. Container is a datatype that can hold more than one elements. In python, contains can be list, tuple, set, and dict.

Lists

The list in python is a linear data structure, it was implemented by doubly linked list behind the scenes in python. List works exactly like an array in other programming languages, and the elements in the list can be accessed using square brackets ([]). Meanwhile, lists are defined using square brackets. Unlike the other programming languages, lists can contain different types of elements. And because of the interpretor, we don’t need to declare the type of variables when we define them. The elements in the list can be duplicate.

# define a list
>>> L = [7, 3, "a", "apple", 1.45, True, -12]
>>> print(L)
[7, 3, 'a', 'apple', 1.45, True, -12]

# python uses 0-based indices to access the element in the list
>>> print(L[3])
apple
# negative index will start at the end
>>> print(L[-2])
True
# if we try to access an index that is out of range, python will throw an error
>>> print(L[8])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

# after defining a list, we can use append() function to add an element to it
>>> L.append(7)
>>> print(L)
[7, 3, 'a', 'apple', 1.45, True, -12, 7]

# we can also use index to update the element
>>> L[1] = 18
>>> print(L)
[7, 18, 'a', 'apple', 1.45, True, -12, 7]

# list combining: we can combine two or more list using the operator "+"
>>> L1 = [1, 2, 3]
>>> L2 = [4, 5, 6]
>>> L3 = L1 + L2
>>> print(L3)
[1, 2, 3, 4, 5, 6]

# list slicing: we can use ":" inside the square brackets to get the part we want
# the rule is <list_name>[start:end(not include):step]
>>> print(L[2:5])
['a', 'apple', 1.45] # from index 2 up to index 5
>>> print(L[2:7:2])
['a', 1.45, -12]  # from index 2 up to index 7 but in the step of 2

# list entanglement: be careful when assigning a list to another variable
>>> x = L
>>> x[0] = 10  # the modification on x will also apply on L
>>> print(L)
[10, 18, 'a', 'apple', 1.45, True, -12, 7]
>>> print(x)
[10, 18, 'a', 'apple', 1.45, True, -12, 7]

# the better way to make a copy is calling the copy() function
>>> y = L.copy()
>>> y[0] = 20
>>> print(L)
[10, 18, 'a', 'apple', 1.45, True, -12, 7]
>>> print(y)
[20, 18, 'a', 'apple', 1.45, True, -12, 7]

# looping a list: instead of using print() function to output the list, we can use a for loop to traverse it
>>> for i in range(len(L)):
...     print(L[i])
...
10
18
a
apple
1.45
True
-12
7

There are some common functions of list can be used: append(), clear(), copy(), count(), extend(), index(), insert(), pop(), remove(), reverse(), and sort().

# append(): add an element at the end of the list
>>> L = [4, 6, 1, 2]
>>> L.append(3)
>>> print(L)
[4, 6, 1, 2, 3]

# insert(index, data): add an element in the given index
>>> L.insert(2, 9)
>>> print(L)
[4, 6, 9, 1, 2, 3]  # the data 9 is added at index 2

# extend(new_list): append the elements in new_list to the original list
>>> L.extend(["apple", "banana"])
>>> print(L)
[4, 6, 9, 1, 2, 3, 'apple', 'banana']

# remove(data): remove the given data in list
>>> L.remove("apple")
>>> print(L)
[4, 6, 9, 1, 2, 3, 'banana']
# if the given data is not in the list, python will throw an error
>>> L.remove("apple")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

# pop(index): we can also use pop() function to remove the element in the given index
# the pop() function will return the element in the given index
>>> L.pop(3)
1
>>> print(L)
[4, 6, 9, 2, 3, 'banana']
# if no index is given, the last element will be removed and returned
>>> L.pop()
'banana'
# if the given index is out of range, python will throw an error
>>> L.pop(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop index out of range

# reverse(): this function will reverse the whole list and return None
>>> print(L)
[4, 6, 9, 2, 3]
>>> L.reverse()
>>> print(L)
[3, 2, 9, 6, 4]

# sort(): this function will sort the whole list in ascending order and return None
# the sorting is in-place, which means the list itself will be modified
>>> L.sort()
>>> print(L)
[2, 3, 4, 6, 9]
# small note: when applying sort() function, if the elements are numbers, 
# the sorting will be based on the value, if the elements are strings, 
# the sorting will be based on acsii. But they can't be mixed.
>>> tmp = [8, 4, 3, "apple"]
>>> tmp.sort()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'

# count(): this function will return the number of occurrences of the given value
>>> nums = [7, 7, 5, "apple", 7, True, 0.7, -12]
>>> print(nums.count(7))
3

# index(data): the function will return the first index of the given data
>>> print(nums.index(7))
0
# we can also defind the starting index
>>> print(nums.index(7, 2)) # start from index 2
4
# if the data is not in the list, python will throw an error
>>> print(nums.index(10))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 10 is not in list

# clear(): this function will remove all the elements in the list and return None
>>> nums.clear()
>>> print(nums)
[]

As lists can contain different type of data, it can also contain lists, which becomes nested lists. We can treat it as multiple dimension arrays. Nested lists are necessary for some applications, like matrix calculations. The element in nested list can be access using multiple square brackets.

>>> L = [ 4, [3, "apple", 0.0], 0.9, [None, True]]
>>> print(L[1][1])
apple

Instead of using square brackets to define a list, we can also use list comprehension to define a list. The rule is: L = [<expressions> for <variable> in <iterable object> if <condition>]

# generate a list of 2^n where 0 <= n <= 10
>>> L = [2 ** n for n in range(11)]
>>> print(L)
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

# generate a list of numbers between 0 and 100 where the number can be divided by 5
>>> L = [k for k in range(100) if k % 5 == 0]
>>> print(L)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]

Tuples

Tuples in python are like lists, but they are IMMUTABLE, which means they can’t be changed once they are defined. We are not able to add or remove elements in the tuples, and we are not able to modify the value of data in tuples. On the other hand, tuples are sequential and the elements can be accessed using indices (like lists).

Small notes on tuple:

  1. tuples are safer than lists, try to use tuple when we don’t expect to change the elements in the sequence.
  2. When we define a variable like this: t = (1), the type of t is int not tuple. If we want to define a tuple with only one element, use the way instead: t = (1, )
  3. The elements in tuples are not able to change. But if a list is in the tuple, any modifications in that list are allowed, except changing the list to another datatype.
# define a tuple and access the element
>>> T = (3, 5, "apple", 0.28, True)
>>> print(T)
(3, 5, 'apple', 0.28, True)
>>> print(T[1])
5

# define a tuple with only 1 element
>>> t = (1)
>>> type(t)
<class 'int'>
>>> t = (1, )
>>> type(t)
<class 'tuple'>

# sequential elements (except tuples) in tuple can be changed
>>> t_nums = (1, 2, 3, [4, 5, 6])
>>> print(t_nums)
(1, 2, 3, [4, 5, 6])
>>> t_nums[3][1] = 8
>>> print(t_nums)
(1, 2, 3, [4, 8, 6])

Sets

Sets are like lists but they don’t have an order and duplicates are ignored. The sets are implemented based on hashed structure. The elements in sets are not duplicated. The same elements added in sets will not throw an error, but make no effect. The aspect of a set can help us remove the repeated elements conveniently. After a set is defined, we can’t change the value of data, but we can add or remove elements inside the set. What’s more, we are not able to access the elements in sets using indices.

# define a set in different ways
>>> S = {3, 6, "apple", "orange", True}
>>> print(S)
{True, 3, 6, 'orange', 'apple'}
>>> S1 = set((3, 6, "apple", "orange", True))
>>> print(S1)
{True, 3, 6, 'orange', 'apple'}

# access the element using index is not allowed
>>> print(S[1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'set' object is not subscriptable

# add element in set
>>> S.add(3.14)
>>> print(S)
{True, 3, 3.14, 6, 'orange', 'apple'}

# add elements using update()
# the update function will take another set as input and update the original set as a union set
>>> print(S)
{True, 3, 3.14, 6, 'orange', 'apple'}
>>> S.update({6, 8, "apple"})
>>> print(S)
{True, 3, 3.14, 6, 'orange', 8, 'apple'}

# remove an element in set
>>> S.remove("apple")
>>> print(S)
{True, 3, 3.14, 6, 'orange', 8}
# if the given data is not in set, python will throw an error

# remove element using pop()
# the pop() function will remove and return the first element in set
>>> print(S)
{True, 3, 3.14, 6, 'orange'}
>>> S.pop()
True
>>> print(S)
{3, 3.14, 6, 'orange', 8}

# remove element using discard()
# the difference between remove and discard is if a given element is not in set
# discard will do nothing
>>> print(S)
{3, 3.14, 6, 'orange', 8}
>>> S.discard(3.14)
>>> print(S)
{3, 6, 'orange', 8}
>>> S.discard(3.14)
>>> print(S)
{3, 6, 'orange', 8}

# get the difference between two sets
>>> print(S)
{3, 6, 'orange', 8}
>>> print(S1)
{True, 3, 6, 'orange', 'apple'}
>>> S.difference(S1) # the element in S but not in S1
{8}
>>> S1. difference(S) # the element in S1 but not in S
{True, 'apple'}

# get the elements in both sets
>>> print(S)
{3, 6, 'orange', 8}
>>> print(S1)
{True, 3, 6, 'orange', 'apple'}
>>> S.intersection(S1)
{3, 6, 'orange'}
>>> S1.intersection(S)
{3, 6, 'orange'}

# get the union set of two sets
>>> print(S)
{3, 6, 'orange', 8}
>>> print(S1)
{True, 3, 6, 'orange', 'apple'}
>>> S.union(S1)
{True, 3, 6, 'orange', 8, 'apple'}

Dictionary

Dictionaries are like hash maps in other programming languages. Dictionaries are ordered (as in python 3.7, unordered in python 3.6 and earlier) and duplicated elements are ignored. A dictionary is defined with curly brackets ({}) filled with key:value pairs. Since the elements in dictionary is in key:value pairs, we can use the keys to access instead of index. On the other hand, the key and value in dictionary are not necessarily in the same data type. What’s more, the key in a dictionary is not duplicated, but the values can be duplicated.

Small note: the key in dictionary can’t be a list.

# define a dictionary in different ways
>>> D = {"apple": "fruit", 3.14: "value of pi"}
>>> print(D)
{'apple': 'fruit', 3.14: 'value of pi'}
>>> D1 = dict("apple"="fruit", 3.14="value of pi", "banana"="fruit"})
>>> print(D1)
{'apple': 'fruit', 3.14: 'value of pi', 'banana': 'fruit'}

# access the element using keys
>>> print(D["apple"])
fruit
# if the key is not in the dictionary, python will throw an error
>>> print(D["banana"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'banana'

# we can check if the key is in the dictionary using "in" keyword
>>> print("apple" in D)
True
>>> print("banana" in D)
False

# change or add element in dictionary using key
# if key is in dictionary, the value will be changed
>>> D["apple"] = "healthy fruit"
>>> print(D)
{'apple': 'healthy fruit', 3.14: 'value of pi'}
# if key is not in dictionary, a new element will be added
>>> print(D)
{'apple': 'healthy fruit', 3.14: 'value of pi', 'banana': 'fruit'}

# remove the key:value pair in dictionary
# 1. use pop(key) function will remove and return the value of the given key
>>> print(D)
{'apple': 'healthy fruit', 3.14: 'value of pi', 'banana': 'fruit'}
>>> D.pop("banana")
'fruit'
>>> print(D)
{'apple': 'healthy fruit', 3.14: 'value of pi'}
# 2. use "del" keyword
>>> del D[3.14]
>>> print(D)
{'apple': 'healthy fruit'}
# both of them will raise an error when the given key is not in the dictionary

# traversing a dict is not as easy as list or tuple
# 1. use keys
>>> for k in D1:
...     print(f"{k}: {D1[k]}")
... 
apple: fruit
3.14: value of pi
banana: fruit
# 2. use key:value iterator
>>> for k,v in D1.items():
...     print(f"{k}: {v}")
... 
apple: fruit
3.14: value of pi
banana: fruit

Leave a Reply

Your email address will not be published. Required fields are marked *