data structures
Table of Contents
there are many types of data structures in python. here are a few:
1. lists
- ordered
- items in a list are called elements, which are accessible via index.
- supports slicing:
- syntax: list_name[start:stop:step]
- mutable, meaning that any changes to a list happen within the same list.
- elements can be of different types.
- use a list when:
- you know you need to make changes to the data collection later on (adding, removing, etc.)
- when the order of the list sequence matters
- the size of the collection is not fixed.
- example: a grocery list
- you need to be able to add items when they come to mind or remove them when you buy something.
- here are some common functions/methods used on lists:
print("indexing into a list:") # list example a_list = ["a", "b", "c"] # use square brackets and indexing to retrieve an element from the list print(a_list[1]) # prints b print() print("getting the length of a list:") # get length of list print(len(a_list)) # prints 3 print() print("getting the indices of a list:") # since we often know the length of a list using the len() function, # we typically use a for loop to iterate through each index in the list. for i in range(len(a_list)): print(i) # prints out: # 0 # 1 # 2 print() print("getting the values of a list via looping by index:") # we can also iterate through each element in the list using indexing. for i in range(len(a_list)): print(a_list[i]) # prints out: # a # b # c print() print("getting the values of a list via looping by value:") # alternatively, we can iterate through each element in the list by value. for element in a_list: print(element) # prints out: # a # b # c print() print("getting indices and corresponding value using enumerate():") # you can simplify looping through iterables (lists, tuples, strings) # when you need both the index and value using the enumerate() function. # side note: the str() function converts an object # (such as a float or integer) into a string. for index, value in enumerate(a_list): print(str(index) + ", " + value) # prints out: # 0, a # 1, b # 2, c print() print("getting type list:") # get the type "list" print(type(a_list)) # prints <class 'list'> print() print("using list constructor to create a list:") # list constructor creates a new list b_list = list((1, 2, 3)) print(b_list) # prints [1, 2, 3] print() print("appending an element to a list:") # adds a new element to the end of the list b_list.append(4) print(b_list) # prints [1, 2, 3, 4] print() print("inserting an element into a list:") # inserts an element at a specific index in the list a_list.insert(1, "d") print(a_list) # prints ["a", "d", "b", "c"] print() print("removing an element from a list:") # removes the first occurrence of a specific value from the list a_list.remove("b") print(a_list) # prints ["a", "d", "c"] print() print("popping an element from a list:") # removes and returns the element at the specific index. # if the index is not provided, then it removes and returns the last element. b_list.pop(1) print(b_list) # prints [1, 3, 4] print() print("popping the last element from a list:") b_list.pop() print(b_list) # prints [1, 3] print() print("sorting a list in-place:") # sorts the elements in-place, modifying the original list. a_list.sort() print(a_list) # prints ["a", "c", "d"] print() print("reversing a list in-place:") # reverses the order of elements in the list in-place. b_list.reverse() print(b_list) # prints [3, 1] print() print("returning the minimum item in a list:") # returns the minimum item in the list print(min(b_list)) # prints 1 print() print("returning the maximum item in a list:") # returns the maximum item in the list print(max(b_list)) # prints 3 print() print("returning the sum of the items in a list:") # returns the sum of the items in the list (supports a mix of floats and integers) print(sum(b_list)) # prints 4 print() print("basic slicing:") c_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # slicing from start to end with default step = 1: print(c_list[2:7]) # prints [2, 3, 4, 5, 6] print() print("slicing with default start:") # slicing from the beginning with default start = 0 and default step = 1: print(c_list[:5]) # prints [0, 1, 2, 3, 4] print() print("slicing with default stop:") # slicing to the end with default stop = (end of list) and default step = 1: print(c_list[4:]) # prints [4, 5, 6, 7, 8, 9] print() print("slicing with step:") # slicing with a step: print("example 1:", c_list[1:9:2]) # prints [1, 3, 5, 7] print("example 2:", c_list[::3]) # prints [0, 3, 6, 9] print() print("slicing with negative indices:") # slicing with negative indices: print("example 1:", c_list[-3:]) # prints [7, 8, 9] print("example 2:", c_list[:-2]) # prints [0, 1, 2, 3, 4, 5, 6, 7] print("example 3:", c_list[-5:-2]) # prints [5, 6, 7] print() print("using slicing to reverse a list:") # using slicing to reverse a list: print(c_list[::-1]) # prints [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
indexing into a list: b getting the length of a list: 3 getting the indices of a list: 0 1 2 getting the values of a list via looping by index: a b c getting the values of a list via looping by value: a b c getting indices and corresponding value using enumerate(): 0, a 1, b 2, c getting type list: <class 'list'> using list constructor to create a list: [1, 2, 3] appending an element to a list: [1, 2, 3, 4] inserting an element into a list: ['a', 'd', 'b', 'c'] removing an element from a list: ['a', 'd', 'c'] popping an element from a list: [1, 3, 4] popping the last element from a list: [1, 3] sorting a list in-place: ['a', 'c', 'd'] reversing a list in-place: [3, 1] returning the minimum item in a list: 1 returning the maximum item in a list: 3 returning the sum of the items in a list: 4 basic slicing: [2, 3, 4, 5, 6] slicing with default start: [0, 1, 2, 3, 4] slicing with default stop: [4, 5, 6, 7, 8, 9] slicing with step: example 1: [1, 3, 5, 7] example 2: [0, 3, 6, 9] slicing with negative indices: example 1: [7, 8, 9] example 2: [0, 1, 2, 3, 4, 5, 6, 7] example 3: [5, 6, 7] using slicing to reverse a list: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
2. tuples
- ordered
- unlike lists, they are immutable, meaning that once a tuple is created, it cannot be changed.
- any changes made to it require creating a copy.
- similar to lists, it can store elements of different data types.
- can access values via indexing.
- use a tuple when:
- the collection of items should not be modified.
- the size of the collection will not change.
- when a function needs to return multiple values.
- example: (x, y, z) coordinates and RGB color values.
- based on what you’re using these for, you might not want to be able to easily change its data.
- tuples tend to have some common or similar functions/methods that can also be used on lists.
- here are some common functions/methods used on tuples:
# tuple example a_tuple = (1, 2, 3, 2, 1) print("indexing into a tuple:") # like lists, tuples also support indexing. print(a_tuple[2]) # prints 3 print() print("counting the number of times a value appears in a tuple:") # counts the number of times a specific value appears within the tuple a_count = a_tuple.count(1) print(a_count) # prints 2 print() print("returns the index of the first occurrence of a specific value:") # searches the tuple for a specific value and returns the index of the first occurrence of it. a_index = a_tuple.index(2) print(a_index) # prints 1 print() print("converting a collection of items into a tuple:") # converts a collection of items into a tuple b_tuple = tuple([5, 4, 3, 2, 1]) print(b_tuple) # prints (5, 4, 3, 2, 1)
indexing into a tuple: 3 counting the number of times a value appears in a tuple: 2 returns the index of the first occurrence of a specific value: 1 converting a collection of items into a tuple: (5, 4, 3, 2, 1)
3. namedtuples
- an extension of regular tuples.
- unlike regular tuples, which use indexing, elements can be accessed via descriptive field names using dot notation.
- helps with code readability.
- similar to regular tuples, namedtuple objects are also immutable.
- in order to make changes, a new copy of the namedtuple must be created.
- there are two main parts of a namedtuple:
- the name. (the very first item when defining a namedtuple)
- the fields/attributes associated with it. (the remaining items when defining a namedtuple AKA the stuff in square brackets)
- use a namedtuple when:
- you want a simple, structured data container.
- you want to group related data together.
- example: a College namedtuple
- may have fields for the college name, faculty population, student population, and mascot affiliated with the college.
- typically, you only want to use namedtuples for objects that are nouns rather than verbs.
- example: chess
- it would be appropriate to have a Piece namedtuple that has the name of the piece tuple (king, queen, bishop, etc) and its coordinates on a 2-dimensional list.
- it wouldn’t be appropriate to have a MovePiece namedtuple because moving a piece is an action rather than a noun. it would make more sense to use something like a function to execute this task.
- example: chess
- here are some common functions/methods used on namedtuples:
# don't forget to import namedtuple from the collections library before implementing namedtuple! from collections import namedtuple print("original Piece namedtuple:") # namedtuple definition example Piece = namedtuple('Piece', ['piece_type', 'x', 'y']) # creation of namedtuple instance example queen = Piece(piece_type="queen", x=1, y=5) # creates and returns a new namedtuple instance with # the specified fields replaced by new values. # make sure to assign it to a variable so that the modified namedtuple can be used later. new_queen = queen._replace(x=2, y=4) print(queen) print() print("modified Piece namedtuple:") # should still print out the original namedtuple, # Piece(piece_type="queen", x=1, y=5) print(new_queen) # prints out the new namedtuple with modified fields, # Piece(piece_type="queen", x=2, y=4) print() print("prints the Piece namedtuple fields, piece type, x, and y, in that order:") # use dot notation to access namedtuple fields print(new_queen.piece_type) # prints queen print(new_queen.x) # prints 2 print(new_queen.y) # prints 4
original Piece namedtuple: Piece(piece_type='queen', x=1, y=5) modified Piece namedtuple: Piece(piece_type='queen', x=2, y=4) prints the Piece namedtuple fields, piece type, x, and y, in that order: queen 2 4
4. sets
- unordered
- sets only store unique elements.
- if you try to add a duplicate element, it will be ignored and the set will remain unchanged.
- mutable (can add or remove elements)
- individual elements in a set must be immutable.
- iterable
- use a set when:
- you need a collection of unique items.
- order of your collection doesn’t matter.
- you want to use mathematical set operations on your collection. (ics 6b type stuff)
- example: a collection of letters.
- useful when you want to have unique, specific alphabet/grammar for a grammar parser.
- here are some common functions/methods used on sets:
print("adds an element to a set:") # set example a_set = {1, 2} # add an element to a set. a_set.add(3) print(a_set) # prints {1, 2, 3} in no particular order. print() print("removes an element from a set:") # removes a specific element from a set. a_set.remove(2) print(a_set) # prints {1, 3} in no particular order. print() print("pops an arbitrary element from a set:") # removes and returns some arbitrary element from the set. a_element = a_set.pop() print(f"popped element: {a_element}") # will either print 1 or 3. print(f"set after popped element: {a_set}") # will either print {1} or {3}. print() print("gets the union of two sets:") # just some new sets set1 = {1, 2} set2 = {2, 3} # returns a new set containing all unique elements from both sets. union_set = set1.union(set2) print(union_set) # prints {1, 2, 3} in no particular order. print() print("gets the intersection of two sets:") # even more new sets set1 = {1, 2, 3} set2 = {2, 3, 4} # returns a new set containing only elements that are in both sets. intersection_set = set1.intersection(set2) print(intersection_set) # prints {2, 3} in no particular order. print() print("gets the difference of two sets:") # yep more new sets... again set1 = {1, 2, 3} set2 = {2, 4} # returns a new set containing elements present in the first set but not in the second set. difference_set = set1.difference(set2) print(difference_set) # prints {1, 3} in no particular order. print() print("gets the symmetric difference of two sets:") # finally... have some more new sets set1 = {1, 2, 3} set2 = {2, 3, 4} symmetric_difference = set1.symmetric_difference(set2) print(symmetric_difference) # prints {1, 4} in no particular order.
adds an element to a set:
{1, 2, 3}
removes an element from a set:
{1, 3}
pops an arbitrary element from a set:
popped element: 1
set after popped element: {3}
gets the union of two sets:
{1, 2, 3}
gets the intersection of two sets:
{2, 3}
gets the difference of two sets:
{1, 3}
gets the symmetric difference of two sets:
{1, 4}
5. dictionaries
- store data as collection of key-value pairs.
- keys must be immutable and unique, but values don’t have to be unique and can be either immutable or mutable.
- therefore, dictionary values can be of any data type.
- mutable
- typically unordered
- use a dictionary when:
- you have two pieces of data associated with each other, which can be used to create key-value pairs.
- order of elements doesn’t matter.
- examples: student dictionary
- where the keys are student IDs and the values are student names.
- since student IDs are unique, they should be used as the key to access a student’s name.
- two students can have the exact same name while still being distinguishable via their student IDs (keys).
- here are some common functions/methods used on dictionaries:
print("retrieves value based on key using get() method:") # dictionary example a_dict = {"a": 1, "b": 2} # returns the value associated with the key. print(a_dict.get("b")) # prints 2 print() print("updating a dictionary with multiple key-value pairs:") # updates the dictionary with key-value pairs from another dictionary. other_dict = {"c": 3, "d": 4} a_dict.update(other_dict) print(a_dict) # prints {"a": 1, "b": 2, "c": 3, "d": 4} in no particular order. print() print("popping a key-value pair:") # removes the item with the specific key and returns its value. d_value = other_dict.pop("d") print(f"popped key: {d_value}") # prints 4 print(f"dictionary after popped key: {other_dict}") # prints {"c": 3} print() print("changing a key's value:") # changes the value associated with a specific key a_dict["a"] = 0 print(a_dict) # prints {"a": 0, "b": 2, "c": 3, "d": 4} in no particular order. print() print("changing multiple keys' values using update() method:") # the update() method can be used to change the value of one or more key-value pairs at once. other_other_dict = {"b": 1, "c": 2, "d": 3} a_dict.update(other_other_dict) print(a_dict) # prints {"a": 0, "b": 1, "c": 2, "d": 3} in no particular order. print() print("using a for loop to iterate through keys:") # you can use for loops to iterate through the keys, values, or keys and values. for key in a_dict: print(key) # prints out: # a # b # c # d print() print("using a for loop and keys() method to iterate through keys:") # you can use the keys() method to retrieve the keys from a dictionary. for key in a_dict.keys(): print(key) # prints out: # a # b # c # d print() print("using values() method to iterate through values:") # you can use the values() method to retrieve the values from a dictionary. for value in a_dict.values(): print(value) # prints out: # 0 # 1 # 2 # 3 print() print("using items() method to iterate through keys and their respective values:") # you can use the items() method to retrieve the keys and their respective values # from a dictionary. # side note: the str() function converts an object # (such as a float or integer) into a string. for key, value in a_dict.items(): print(key + ", " + str(value)) # prints out: # a, 0 # b, 1 # c, 2 # d, 3
retrieves value based on key using get() method:
2
updating a dictionary with multiple key-value pairs:
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
popping a key-value pair:
popped key: 4
dictionary after popped key: {'c': 3}
changing a key's value:
{'a': 0, 'b': 2, 'c': 3, 'd': 4}
changing multiple keys' values using update() method:
{'a': 0, 'b': 1, 'c': 2, 'd': 3}
using a for loop to iterate through keys:
a
b
c
d
using a for loop and keys() method to iterate through keys:
a
b
c
d
using values() method to iterate through values:
0
1
2
3
using items() method to iterate through keys and their respective values:
a, 0
b, 1
c, 2
d, 3