Python3 Data Structures
In this chapter, we mainly combine the knowledge points learned earlier to introduce Python data structures.
Lists
In Python, lists are mutable, which is the most important feature that distinguishes them from strings and tuples. In one sentence: lists can be modified, while strings and tuples cannot.
The following are the methods of lists in Python:
| Method | Description |
|---|---|
| list.append(x) | Adds an element to the end of the list, equivalent to a[len(a):] = [x]. |
| list.extend(L) | Extends the list by adding all elements of the specified list, equivalent to a[len(a):] = L. |
| list.insert(i, x) | Inserts an element at a specified position. The first parameter is the index of the element before which to insert. For example, a.insert(0, x) inserts at the beginning of the entire list, and a.insert(len(a), x) is equivalent to a.append(x). |
| list.remove(x) | Removes the first element in the list whose value is x. If there is no such element, an error is returned. |
| list.pop([i]) | Removes the element at the specified position from the list and returns it. If no index is specified, a.pop() returns the last element. The element is immediately removed from the list. (The square brackets around i in the method indicate that this parameter is optional, not that you should type a pair of square brackets. You will often see this notation in the Python library reference manual.) |
| list.clear() | Removes all items from the list, equivalent to del a[:]. |
| list.index(x) | Returns the index of the first element in the list whose value is x. If there is no matching element, an error is returned. |
| list.count(x) | Returns the number of times x appears in the list. |
| list.sort() | Sorts the elements in the list. |
| list.reverse() | Reverses the elements in the list. |
| list.copy() | Returns a shallow copy of the list, equivalent to a[:]. |
The following example demonstrates most of the methods of lists:
Example
>>> print(a.count(333), a.count(66.25), a.count('x'))
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
Note: Methods like insert, remove, or sort that modify lists do not return a value.
Using Lists as Stacks
In Python, you can use a list to implement stack functionality. A stack is a last-in-first-out (LIFO, Last-In-First-Out) data structure, meaning the last element added is the first one removed. Lists provide some methods that make them very suitable for stack operations, especiallyappend()andpop()methods.
Use the append() method to add an element to the top of the stack, and use the pop() method without an index to release an element from the top of the stack.
Stack Operations
- Push: Add an element to the top of the stack.
- Pop: Remove and return the top element of the stack.
- Peek/Top: Return the top element of the stack without removing it.
- IsEmpty: Check whether the stack is empty.
- Size: Get the number of elements in the stack.
The following is a detailed explanation of how to implement these operations using lists in Python:
1. Create an empty stack
Example
2. Push operation
Use the append() method to add an element to the top of the stack:
Example
stack.append(2)
stack.append(3)
print(stack) # Output: [1, 2, 3]
3. Pop operation
Use the pop() method to remove and return the top element of the stack:
Example
print(top_element) # Output: 3
print(stack) # Output: [1, 2]
4. View the top element of the stack (Peek/Top)
Directly access the last element of the list (without removing it):
Example
print(top_element) # Output: 2
5. Check if it is empty (IsEmpty)
Check whether the list is empty:
Example
print(is_empty) # Output: False
6. Get the size of the stack (Size)
Use the len() function to get the number of elements in the stack:
Example
print(size) # Output: 2
Example
The following is a complete example showing how to use the above operations to implement a simple stack:
Example
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item)
def pop(self):
if not self.is_empty():
return self.stack.pop()
else:
raise IndexError("pop from empty stack")
def peek(self):
if not self.is_empty():
return self.stack[-1]
else:
raise IndexError("peek from empty stack")
def is_empty(self):
return len(self.stack) == 0
def size(self):
return len(self.stack)
# Usage example
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print("Top element:", stack.peek()) # Output: Top element: 3
print("Stack size:", stack.size()) # Output: Stack size: 3
print("Popped element:", stack.pop()) # Output: Popped element: 3
print("Is the stack empty:", stack.is_empty()) # Output: Is the stack empty: False
print("Stack size:", stack.size()) # Output: Stack size: 2
In the above code, we defined a Stack class that encapsulates a list as the underlying data structure and implements the basic operations of a stack.
The output is as follows:
栈顶元素: 3 栈大小: 3 弹出元素: 3 栈是否为空: False 栈大小: 2
Using Lists as Queues
In Python, a list can be used as a queue, but due to the characteristics of lists, directly using a list to implement a queue is not the optimal choice.
A queue is a first-in-first-out (FIFO, First-In-First-Out) data structure, meaning the earliest added element is removed first.
When using a list, if you frequently insert or delete elements at the beginning of the list, performance will be affected because the time complexity of these operations is O(n). To solve this problem, Python provides collections.deque, which is a double-ended queue that can efficiently add and remove elements at both ends.
Implementing a Queue with collections.deque
collections.deque is part of the Python standard library and is very suitable for implementing queues.
The following is an example of implementing a queue with deque:
Example
# Create an empty queue
queue = deque()
# Add elements to the tail of the queue
queue.append('a')
queue.append('b')
queue.append('c')
print("Queue status:", queue) # Output: Queue status: deque(['a', 'b', 'c'])
# Remove an element from the head of the queue
first_element = queue.popleft()
print("Removed element:", first_element) # Output: Removed element: a
print("Queue status:", queue) # Output: Queue status: deque(['b', 'c'])
# View the head element (without removing it)
front_element = queue[0]
print("Head element:", front_element) # Output: Head element: b
# Check whether the queue is empty
is_empty = len(queue) == 0
print("Is the queue empty:", is_empty) # Output: Is the queue empty: False
# Get the queue size
size = len(queue)
print("Queue size:", size) # Output: Queue size: 2
Implementing a Queue with Lists
Although deque is more efficient, if you insist on using a list to implement a queue, you can still do so. The following is an example of how to use a list to implement a queue:
1. Create a queue
Example
2. Add elements to the tail of the queue
Use the append() method to add elements to the end of the queue:
Example
queue.append('b')
queue.append('c')
print("Queue status:", queue) # Output: Queue status: ['a', 'b', 'c']
3. Remove elements from the front of the queue
Use the pop(0) method to remove elements from the front of the queue:
Example
print("Removed element:", first_element) # Output: Removed element: a
print("Queue status:", queue) # Output: Queue status: ['b', 'c']
4. View the front element of the queue (without removing it)
Directly access the first element of the list:
Example
print("Front element:", front_element) # Output: Front element: b
5. Check whether the queue is empty
Check whether the list is empty:
Example
print("Is the queue empty:", is_empty) # Output: Is the queue empty: False
6. Get the queue size
Use the len() function to get the size of the queue:
Example
print("Queue size:", size) # Output: Queue size: 2
Example (Implementing a queue with lists)
Example
class Queue:
def __init__(self):
self.queue = []
def enqueue(self, item):
self.queue.append(item)
def dequeue(self):
if not self.is_empty():
return self.queue.pop(0)
else:
raise IndexError("dequeue from empty queue")
def peek(self):
if not self.is_empty():
return self.queue[0]
else:
raise IndexError("peek from empty queue")
def is_empty(self):
return len(self.queue) == 0
def size(self):
return len(self.queue)
# Usage example
queue = Queue()
queue.enqueue('a')
queue.enqueue('b')
queue.enqueue('c')
print("Front element:", queue.peek()) # Output: Front element: a
print("Queue size:", queue.size()) # Output: Queue size: 3
print("Removed element:", queue.dequeue()) # Output: Removed element: a
print("Is the queue empty:", queue.is_empty()) # Output: Is the queue empty: False
print("Queue size:", queue.size()) # Output: Queue size: 2
Although you can use a list to implement a queue, using collections.deque is more efficient and concise. It provides O(1) time complexity for add and remove operations, making it very suitable for a queue data structure.
List Comprehensions
List comprehensions provide a concise way to create lists from sequences. Usually an application applies some operations to each element of a sequence, using the results as elements of a new list, or creates subsequences based on determined conditions.
A list comprehension consists of an expression followed by a for clause, then zero or more for or if clauses. The result is a list generated from the expression in the context of the following for and if clauses. If you want the expression to produce a tuple, you must use parentheses.
Here we multiply each value in the list by three to obtain a new list:
>>> [3*x for x in vec]
[6, 12, 18]
Now let's play with a few tricks:
[[2, 4], [4, 16], [6, 36]]
Here we call a method on each element of the sequence one by one:
Example
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
We can use an if clause as a filter:
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
The following are some demonstrations of loops and other techniques:
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
[8, 12, -54]
List comprehensions can use complex expressions or nested functions:
['3.1', '3.14', '3.142', '3.1416', '3.14159']
Nested List Comprehensions
Python lists can also be nested.
The following example shows a 3X4 matrix list:
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
The following example converts the 3X4 matrix list into a 4X3 list:
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
The above example can also be implemented using the following method:
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Another implementation method:
>>> for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
The del statement
The del statement can be used to remove an element from a list by index rather than by value. This is different from using pop(), which returns a value. The del statement can be used to remove a slice from a list, or to clear the entire list (the method we introduced earlier is to assign an empty list to that slice). For example:
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
You can also use del to delete variables:
>>> del a
Tuples and Sequences
A tuple consists of a number of values separated by commas, for example:
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
As you can see, tuples are always enclosed in parentheses in output so that nested structures are expressed correctly. They may be entered with or without parentheses, though parentheses are usually necessary if the tuple is part of a larger expression.
Sets
A set is an unordered collection of unique elements. Basic features include relation testing and eliminating duplicate elements.
Sets can be created with curly braces ({}). Note: To create an empty set, you must use set() instead of {}; the latter creates an empty dictionary, a data structure we will introduce in the next section.
The following is a simple demonstration:
>>> print(basket) # Remove duplicates
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # Check membership
True
>>> 'crabgrass' in basket
False
>>> # The following demonstrates operations on two sets
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # Unique letters in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # Letters in a but not in b
{'r', 'd', 'b'}
>>> a | b # Letters in a or b
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # Letters in both a and b
{'a', 'c'}
>>> a ^ b # Letters in a or b but not in both
{'r', 'd', 'b', 'm', 'z', 'l'}
Sets also support comprehensions:
>>> a
{'r', 'd'}
Dictionaries
Another very useful built-in data type in Python is the dictionary.
Sequences are indexed by consecutive integers; by contrast, dictionaries are indexed by keys, which can be any immutable type, usually strings or numbers.
The best way to understand a dictionary is to view it as an unordered collection of key=>value pairs. Within the same dictionary, the keys must be distinct from one another.
A pair of curly braces creates an empty dictionary: {}.
This is a simple example of using a dictionary:
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> list(tel.keys())
['irv', 'guido', 'jack']
>>> sorted(tel.keys())
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
The dict() constructor builds dictionaries directly from a list of key-value pairs. If there is a fixed pattern, list comprehensions can specify particular key-value pairs:
{'sape': 4139, 'jack': 4098, 'guido': 4127}
In addition, dictionary comprehensions can be used to create dictionaries with arbitrary key and value expressions:
{2: 4, 4: 16, 6: 36}
If the keys are simple strings, it is sometimes more convenient to specify key-value pairs using keyword arguments:
{'sape': 4139, 'jack': 4098, 'guido': 4127}
Looping Techniques
When looping through a dictionary, the keys and corresponding values can be retrieved at the same time using the items() method:
>>> for k, v in knights.items():
... print(k, v)
...
gallahad the pure
robin the brave
When looping through a sequence, the index position and corresponding value can be obtained at the same time using the enumerate() function:
... print(i, v)
...
0 tic
1 tac
2 toe
To loop over two or more sequences at the same time, use zip() to combine them:
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(q, a))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
To loop over a sequence in reverse, first specify the sequence and then call the reversed() function:
... print(i)
...
9
7
5
3
1
To loop over a sequence in order, use the sorted() function, which returns a sorted sequence without modifying the original:
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear