Skip to content

Commit a0eb58e

Browse files
committed
chore: Refactor Stack class and add new features
1 parent 64d72b3 commit a0eb58e

File tree

2 files changed

+166
-20
lines changed

2 files changed

+166
-20
lines changed

data_structures/stack/stack.py

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,50 @@
11
class Stack:
2-
"""
3-
Simple stack implementation using a list
4-
"""
52

6-
def __init__(self) -> None:
7-
self.items = []
3+
def __init__(self, max_size=None):
4+
self._items = []
5+
self._max_size = max_size
86

9-
def is_empty(self) -> bool:
10-
"""check if the stack is empty"""
11-
return self.items == []
7+
def push(self, item):
8+
if self._max_size is not None and len(self._items) >= self._max_size:
9+
raise OverflowError("Stack has reached its maximum size")
10+
self._items.append(item)
1211

13-
def push(self, item: any) -> None:
14-
"""push an item to the top of the stack"""
15-
self.items.append(item)
12+
def pop(self):
13+
if self.is_empty():
14+
raise IndexError("Stack is empty")
15+
return self._items.pop()
1616

17-
def pop(self) -> any:
18-
"""pop the top item of the stack"""
19-
return self.items.pop()
17+
def peek(self):
18+
if self.is_empty():
19+
raise IndexError("Stack is empty")
20+
return self._items[-1]
2021

21-
def peek(self) -> any:
22-
"""peek at the top item of the stack"""
23-
return self.items[len(self.items)-1]
22+
def is_empty(self):
23+
return not bool(self._items)
2424

25-
def size(self) -> int:
26-
"""return the size of the stack"""
27-
return len(self.items)
25+
def size(self):
26+
return len(self._items)
27+
28+
def clear(self):
29+
self._items.clear()
30+
31+
def copy(self):
32+
new_stack = Stack(max_size=self._max_size)
33+
new_stack._items = self._items.copy()
34+
return new_stack
35+
36+
def __eq__(self, other):
37+
if not isinstance(other, Stack):
38+
return NotImplemented
39+
return self._items == other._items and self._max_size == other._max_size
40+
41+
def __iter__(self):
42+
return reversed(self._items)
43+
44+
def __repr__(self):
45+
return (
46+
f"{self.__class__.__name__}(size={self.size()}, max_size={self._max_size})"
47+
)
48+
49+
def __bool__(self):
50+
return bool(self._items)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import unittest
2+
from .stack import Stack
3+
import sys
4+
import time
5+
6+
7+
class TestStack(unittest.TestCase):
8+
def setUp(self):
9+
self.stack = Stack()
10+
11+
def test_push_pop_large_number_of_items(self):
12+
"""Test pushing and popping a large number of items."""
13+
num_items = 1000000
14+
for i in range(num_items):
15+
self.stack.push(i)
16+
self.assertEqual(self.stack.size(), num_items)
17+
for i in reversed(range(num_items)):
18+
self.assertEqual(self.stack.pop(), i)
19+
self.assertTrue(self.stack.is_empty())
20+
21+
def test_push_pop_performance(self):
22+
"""Test the performance of push and pop operations."""
23+
num_operations = 100000
24+
start_time = time.time()
25+
for i in range(num_operations):
26+
self.stack.push(i)
27+
for _ in range(num_operations):
28+
self.stack.pop()
29+
end_time = time.time()
30+
self.assertLess(
31+
end_time - start_time, 1.0
32+
) # Assert that 100,000 push/pop operations take less than 1 second
33+
34+
def test_memory_usage(self):
35+
"""Test that the stack doesn't use excessive memory."""
36+
initial_memory = sys.getsizeof(self.stack)
37+
for i in range(1000):
38+
self.stack.push(i)
39+
final_memory = sys.getsizeof(self.stack)
40+
self.assertLess(
41+
final_memory - initial_memory, 10000
42+
) # Assert that 1000 pushes use less than 10KB additional memory
43+
44+
def test_push_different_types(self):
45+
"""Test pushing items of different types onto the stack."""
46+
items = [1, "string", [1, 2, 3], {"key": "value"}, (1, 2), set([1, 2, 3])]
47+
for item in items:
48+
self.stack.push(item)
49+
for item in reversed(items):
50+
self.assertEqual(self.stack.pop(), item)
51+
52+
def test_clear_stack(self):
53+
"""Test clearing the stack."""
54+
for i in range(10):
55+
self.stack.push(i)
56+
self.stack.clear()
57+
self.assertTrue(self.stack.is_empty())
58+
self.assertEqual(self.stack.size(), 0)
59+
60+
def test_push_pop_alternating(self):
61+
"""Test alternating push and pop operations."""
62+
for i in range(100):
63+
self.stack.push(i)
64+
if i % 2 == 0:
65+
self.assertEqual(self.stack.pop(), i)
66+
self.assertEqual(self.stack.size(), 50)
67+
68+
def test_stack_with_none_values(self):
69+
"""Test stack operations with None values."""
70+
self.stack.push(None)
71+
self.assertEqual(self.stack.size(), 1)
72+
self.assertIsNone(self.stack.peek())
73+
self.assertIsNone(self.stack.pop())
74+
75+
def test_stack_representation(self):
76+
"""Test the string representation of the stack."""
77+
for i in range(5):
78+
self.stack.push(i)
79+
self.assertIn("Stack", str(self.stack))
80+
self.assertIn("5", str(self.stack)) # Size should be mentioned
81+
82+
def test_stack_copy(self):
83+
"""Test creating a copy of the stack."""
84+
for i in range(5):
85+
self.stack.push(i)
86+
stack_copy = self.stack.copy()
87+
self.assertEqual(self.stack.size(), stack_copy.size())
88+
while not self.stack.is_empty():
89+
self.assertEqual(self.stack.pop(), stack_copy.pop())
90+
91+
def test_stack_comparison(self):
92+
"""Test comparing two stacks."""
93+
stack1 = Stack()
94+
stack2 = Stack()
95+
for i in range(5):
96+
stack1.push(i)
97+
stack2.push(i)
98+
self.assertEqual(stack1, stack2)
99+
stack1.push(5)
100+
self.assertNotEqual(stack1, stack2)
101+
102+
def test_stack_iterable(self):
103+
"""Test if the stack is iterable."""
104+
items = list(range(5))
105+
for item in items:
106+
self.stack.push(item)
107+
self.assertEqual(list(self.stack), list(reversed(items)))
108+
109+
def test_stack_max_size(self):
110+
"""Test stack with a maximum size limit."""
111+
max_size = 5
112+
limited_stack = Stack(max_size=max_size)
113+
for i in range(10):
114+
if i < max_size:
115+
limited_stack.push(i)
116+
else:
117+
with self.assertRaises(OverflowError):
118+
limited_stack.push(i)
119+
self.assertEqual(limited_stack.size(), max_size)
120+
121+
122+
if __name__ == "__main__":
123+
unittest.main()

0 commit comments

Comments
 (0)