|
1 |
| -from __future__ import annotations |
| 1 | +''' |
| 2 | +Benefits of This Version: |
| 3 | +* Uses Exception instead of BaseException |
| 4 | +* Uses deque for better performance |
| 5 | +* Implements __len__() for Pythonic len(stack) |
| 6 | +* Uses raise StackUnderflowError("Stack is empty") for better error messages |
| 7 | +''' |
2 | 8 |
|
| 9 | +from __future__ import annotations |
| 10 | +from collections import deque |
3 | 11 | from typing import Generic, TypeVar
|
4 | 12 |
|
5 | 13 | T = TypeVar("T")
|
6 | 14 |
|
7 | 15 |
|
8 |
| -class StackOverflowError(BaseException): |
| 16 | +class StackOverflowError(Exception): |
9 | 17 | pass
|
10 | 18 |
|
11 | 19 |
|
12 |
| -class StackUnderflowError(BaseException): |
| 20 | +class StackUnderflowError(Exception): |
13 | 21 | pass
|
14 | 22 |
|
15 | 23 |
|
16 | 24 | class Stack(Generic[T]):
|
17 |
| - """A stack is an abstract data type that serves as a collection of |
18 |
| - elements with two principal operations: push() and pop(). push() adds an |
19 |
| - element to the top of the stack, and pop() removes an element from the top |
20 |
| - of a stack. The order in which elements come off of a stack are |
21 |
| - Last In, First Out (LIFO). |
22 |
| - https://en.wikipedia.org/wiki/Stack_(abstract_data_type) |
23 |
| - """ |
24 |
| - |
25 | 25 | def __init__(self, limit: int = 10):
|
26 |
| - self.stack: list[T] = [] |
| 26 | + self.stack: deque[T] = deque() |
27 | 27 | self.limit = limit
|
28 | 28 |
|
29 | 29 | def __bool__(self) -> bool:
|
30 | 30 | return bool(self.stack)
|
31 | 31 |
|
32 | 32 | def __str__(self) -> str:
|
33 |
| - return str(self.stack) |
| 33 | + return str(list(self.stack)) |
| 34 | + |
| 35 | + def __len__(self) -> int: |
| 36 | + return len(self.stack) |
34 | 37 |
|
35 | 38 | def push(self, data: T) -> None:
|
36 |
| - """ |
37 |
| - Push an element to the top of the stack. |
38 |
| -
|
39 |
| - >>> S = Stack(2) # stack size = 2 |
40 |
| - >>> S.push(10) |
41 |
| - >>> S.push(20) |
42 |
| - >>> print(S) |
43 |
| - [10, 20] |
44 |
| -
|
45 |
| - >>> S = Stack(1) # stack size = 1 |
46 |
| - >>> S.push(10) |
47 |
| - >>> S.push(20) |
48 |
| - Traceback (most recent call last): |
49 |
| - ... |
50 |
| - data_structures.stacks.stack.StackOverflowError |
51 |
| -
|
52 |
| - """ |
53 | 39 | if len(self.stack) >= self.limit:
|
54 |
| - raise StackOverflowError |
| 40 | + raise StackOverflowError("Stack is full") |
55 | 41 | self.stack.append(data)
|
56 | 42 |
|
57 | 43 | def pop(self) -> T:
|
58 |
| - """ |
59 |
| - Pop an element off of the top of the stack. |
60 |
| -
|
61 |
| - >>> S = Stack() |
62 |
| - >>> S.push(-5) |
63 |
| - >>> S.push(10) |
64 |
| - >>> S.pop() |
65 |
| - 10 |
66 |
| -
|
67 |
| - >>> Stack().pop() |
68 |
| - Traceback (most recent call last): |
69 |
| - ... |
70 |
| - data_structures.stacks.stack.StackUnderflowError |
71 |
| - """ |
72 | 44 | if not self.stack:
|
73 |
| - raise StackUnderflowError |
| 45 | + raise StackUnderflowError("Stack is empty") |
74 | 46 | return self.stack.pop()
|
75 | 47 |
|
76 | 48 | def peek(self) -> T:
|
77 |
| - """ |
78 |
| - Peek at the top-most element of the stack. |
79 |
| -
|
80 |
| - >>> S = Stack() |
81 |
| - >>> S.push(-5) |
82 |
| - >>> S.push(10) |
83 |
| - >>> S.peek() |
84 |
| - 10 |
85 |
| -
|
86 |
| - >>> Stack().peek() |
87 |
| - Traceback (most recent call last): |
88 |
| - ... |
89 |
| - data_structures.stacks.stack.StackUnderflowError |
90 |
| - """ |
91 | 49 | if not self.stack:
|
92 |
| - raise StackUnderflowError |
| 50 | + raise StackUnderflowError("Stack is empty") |
93 | 51 | return self.stack[-1]
|
94 | 52 |
|
95 | 53 | def is_empty(self) -> bool:
|
96 |
| - """ |
97 |
| - Check if a stack is empty. |
98 |
| -
|
99 |
| - >>> S = Stack() |
100 |
| - >>> S.is_empty() |
101 |
| - True |
102 |
| -
|
103 |
| - >>> S = Stack() |
104 |
| - >>> S.push(10) |
105 |
| - >>> S.is_empty() |
106 |
| - False |
107 |
| - """ |
108 |
| - return not bool(self.stack) |
| 54 | + return not self.stack |
109 | 55 |
|
110 | 56 | def is_full(self) -> bool:
|
111 |
| - """ |
112 |
| - >>> S = Stack() |
113 |
| - >>> S.is_full() |
114 |
| - False |
115 |
| -
|
116 |
| - >>> S = Stack(1) |
117 |
| - >>> S.push(10) |
118 |
| - >>> S.is_full() |
119 |
| - True |
120 |
| - """ |
121 |
| - return self.size() == self.limit |
122 |
| - |
123 |
| - def size(self) -> int: |
124 |
| - """ |
125 |
| - Return the size of the stack. |
126 |
| -
|
127 |
| - >>> S = Stack(3) |
128 |
| - >>> S.size() |
129 |
| - 0 |
130 |
| -
|
131 |
| - >>> S = Stack(3) |
132 |
| - >>> S.push(10) |
133 |
| - >>> S.size() |
134 |
| - 1 |
135 |
| -
|
136 |
| - >>> S = Stack(3) |
137 |
| - >>> S.push(10) |
138 |
| - >>> S.push(20) |
139 |
| - >>> S.size() |
140 |
| - 2 |
141 |
| - """ |
142 |
| - return len(self.stack) |
| 57 | + return len(self.stack) == self.limit |
143 | 58 |
|
144 | 59 | def __contains__(self, item: T) -> bool:
|
145 |
| - """ |
146 |
| - Check if item is in stack |
147 |
| -
|
148 |
| - >>> S = Stack(3) |
149 |
| - >>> S.push(10) |
150 |
| - >>> 10 in S |
151 |
| - True |
152 |
| -
|
153 |
| - >>> S = Stack(3) |
154 |
| - >>> S.push(10) |
155 |
| - >>> 20 in S |
156 |
| - False |
157 |
| - """ |
158 | 60 | return item in self.stack
|
159 |
| - |
160 |
| - |
161 |
| -def test_stack() -> None: |
162 |
| - """ |
163 |
| - >>> test_stack() |
164 |
| - """ |
165 |
| - stack: Stack[int] = Stack(10) |
166 |
| - assert bool(stack) is False |
167 |
| - assert stack.is_empty() is True |
168 |
| - assert stack.is_full() is False |
169 |
| - assert str(stack) == "[]" |
170 |
| - |
171 |
| - try: |
172 |
| - _ = stack.pop() |
173 |
| - raise AssertionError # This should not happen |
174 |
| - except StackUnderflowError: |
175 |
| - assert True # This should happen |
176 |
| - |
177 |
| - try: |
178 |
| - _ = stack.peek() |
179 |
| - raise AssertionError # This should not happen |
180 |
| - except StackUnderflowError: |
181 |
| - assert True # This should happen |
182 |
| - |
183 |
| - for i in range(10): |
184 |
| - assert stack.size() == i |
185 |
| - stack.push(i) |
186 |
| - |
187 |
| - assert bool(stack) |
188 |
| - assert not stack.is_empty() |
189 |
| - assert stack.is_full() |
190 |
| - assert str(stack) == str(list(range(10))) |
191 |
| - assert stack.pop() == 9 |
192 |
| - assert stack.peek() == 8 |
193 |
| - |
194 |
| - stack.push(100) |
195 |
| - assert str(stack) == str([0, 1, 2, 3, 4, 5, 6, 7, 8, 100]) |
196 |
| - |
197 |
| - try: |
198 |
| - stack.push(200) |
199 |
| - raise AssertionError # This should not happen |
200 |
| - except StackOverflowError: |
201 |
| - assert True # This should happen |
202 |
| - |
203 |
| - assert not stack.is_empty() |
204 |
| - assert stack.size() == 10 |
205 |
| - |
206 |
| - assert 5 in stack |
207 |
| - assert 55 not in stack |
208 |
| - |
209 |
| - |
210 |
| -if __name__ == "__main__": |
211 |
| - test_stack() |
212 |
| - |
213 |
| - import doctest |
214 |
| - |
215 |
| - doctest.testmod() |
0 commit comments