Skip to content

Commit 184bb82

Browse files
refactor: use *args for array construction instead of iterables
DynamicArray(1, 2, 3) instead of DynamicArray([1, 2, 3]). StaticArray(1, 2, 3) for values, StaticArray(capacity=5) for empty.
1 parent 023c5bc commit 184bb82

8 files changed

Lines changed: 69 additions & 82 deletions

File tree

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,23 @@ All structures implement common Python protocols: `len()`, `in`, `bool()`, `==`,
3434

3535
\* amortised
3636

37-
**Features:** negative indexing, slicing (`arr[1:3]`, `arr[::-1]`), construction from iterables.
37+
**Features:** negative indexing, slicing (`arr[1:3]`, `arr[::-1]`), `*args` construction.
3838

3939
```python
4040
from data_structures.array.static_array import StaticArray
4141
from data_structures.array.dynamic_array import DynamicArray
4242

4343
# Static array — fixed capacity
44-
sa = StaticArray([1, 2, 3])
44+
sa = StaticArray(1, 2, 3)
4545
sa[0] # 1
4646
sa[-1] # 3
4747
sa[::2] # [1, 3]
4848

4949
# Dynamic array — grows and shrinks automatically
50-
da = DynamicArray()
51-
for i in range(1, 6):
52-
da.append(i)
50+
da = DynamicArray(1, 2, 3, 4, 5)
5351
str(da) # "[1, 2, 3, 4, 5]"
5452
da[::-1] # [5, 4, 3, 2, 1]
55-
da == StaticArray([1, 2, 3, 4, 5]) # True (cross-type equality)
53+
da == StaticArray(1, 2, 3, 4, 5) # True (cross-type equality)
5654
```
5755

5856
### Linked Lists

data_structures/array/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class IArray[T](ABC):
66
"""Abstract Array interface (random-access, indexed collection).
77
88
>>> from data_structures.array.static_array import StaticArray
9-
>>> a = StaticArray(4)
9+
>>> a = StaticArray(capacity=4)
1010
>>> a.append(10); a.append(20); a.append(30)
1111
>>> a[1]
1212
20

data_structures/array/dynamic_array.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from collections.abc import Iterable
21
from typing import Iterator, overload
32

43
from .base import IArray
@@ -18,15 +17,15 @@ class DynamicArray[T](IArray[T]):
1817
elements between shrink thresholds.
1918
"""
2019

21-
def __init__(self, values: Iterable[T] | None = None) -> None:
22-
"""Create a dynamic array, optionally pre-filled from *values*.
20+
def __init__(self, *values: T) -> None:
21+
"""Create a dynamic array, optionally pre-filled with *values*.
2322
2423
>>> DynamicArray() # empty
2524
DynamicArray([])
26-
>>> DynamicArray([1, 2, 3]) # pre-filled
25+
>>> DynamicArray(1, 2, 3) # pre-filled
2726
DynamicArray([1, 2, 3])
2827
"""
29-
if values is None:
28+
if not values:
3029
self._capacity: int = _DEFAULT_CAPACITY
3130
self._size: int = 0
3231
self._data: list[T | None] = [None] * self._capacity

data_structures/array/static_array.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from collections.abc import Iterable
21
from typing import Iterator, overload
32

43
from .base import IArray
@@ -10,31 +9,33 @@ class StaticArray[T](IArray[T]):
109
Capacity is set at construction time and cannot grow.
1110
Raises ``OverflowError`` when appending to a full array.
1211
13-
>>> StaticArray(3) # empty with capacity 3
12+
>>> StaticArray(capacity=3) # empty with capacity 3
1413
StaticArray([])
15-
>>> StaticArray([1, 2, 3]) # pre-filled, capacity == len
14+
>>> StaticArray(1, 2, 3) # pre-filled, capacity == len
1615
StaticArray([1, 2, 3])
1716
"""
1817

19-
def __init__(self, capacity_or_values: int | Iterable[T]) -> None:
18+
def __init__(self, *values: T, capacity: int | None = None) -> None:
2019
"""Create a static array.
2120
22-
Pass an ``int`` for an empty array with that capacity, or an
23-
iterable to pre-fill (capacity equals the number of elements).
21+
Pass values as positional args to pre-fill (capacity equals the
22+
number of elements), or use ``capacity`` for an empty array with
23+
a fixed size.
2424
"""
25-
if isinstance(capacity_or_values, int):
26-
if capacity_or_values <= 0:
25+
if values and capacity is not None:
26+
raise TypeError("Cannot specify both values and capacity")
27+
if values:
28+
self._capacity: int = len(values)
29+
self._size: int = len(values)
30+
self._data: list[T | None] = list(values)
31+
elif capacity is not None:
32+
if capacity <= 0:
2733
raise ValueError("Capacity must be positive")
28-
self._capacity: int = capacity_or_values
29-
self._size: int = 0
30-
self._data: list[T | None] = [None] * self._capacity
34+
self._capacity = capacity
35+
self._size = 0
36+
self._data = [None] * self._capacity
3137
else:
32-
values = list(capacity_or_values)
33-
if not values:
34-
raise ValueError("Capacity must be positive")
35-
self._capacity = len(values)
36-
self._size = len(values)
37-
self._data = list(values)
38+
raise TypeError("Provide values or capacity")
3839

3940
def append(self, value: T) -> None:
4041
"""Append *value* after the last element. O(1).

tests/test_arrays.py

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from data_structures.array.static_array import StaticArray
55

66

7-
@pytest.fixture(params=[lambda: StaticArray(5), DynamicArray])
7+
@pytest.fixture(params=[lambda: StaticArray(capacity=5), DynamicArray])
88
def arr(request):
99
return request.param()
1010

@@ -168,20 +168,20 @@ def test_bool_non_empty(arr):
168168
def test_eq_same_elements(arr):
169169
for v in [1, 2, 3]:
170170
arr.append(v)
171-
other = DynamicArray([1, 2, 3])
171+
other = DynamicArray(1, 2, 3)
172172
assert arr == other
173173

174174

175175
def test_eq_different_elements(arr):
176176
for v in [1, 2, 3]:
177177
arr.append(v)
178-
other = DynamicArray([1, 2, 4])
178+
other = DynamicArray(1, 2, 4)
179179
assert arr != other
180180

181181

182182
def test_eq_different_length(arr):
183183
arr.append(1)
184-
other = DynamicArray([1, 2])
184+
other = DynamicArray(1, 2)
185185
assert arr != other
186186

187187

@@ -191,8 +191,8 @@ def test_eq_not_array(arr):
191191

192192

193193
def test_eq_cross_type():
194-
sa = StaticArray([1, 2, 3])
195-
da = DynamicArray([1, 2, 3])
194+
sa = StaticArray(1, 2, 3)
195+
da = DynamicArray(1, 2, 3)
196196
assert sa == da
197197

198198

@@ -249,72 +249,66 @@ def test_slice_empty_result(arr):
249249

250250

251251
def test_static_capacity():
252-
arr = StaticArray(5)
252+
arr = StaticArray(capacity=5)
253253
assert arr.capacity == 5
254254

255255

256256
def test_static_zero_capacity_raises():
257257
with pytest.raises(ValueError):
258-
StaticArray(0)
258+
StaticArray(capacity=0)
259259

260260

261261
def test_static_negative_capacity_raises():
262262
with pytest.raises(ValueError):
263-
StaticArray(-1)
263+
StaticArray(capacity=-1)
264264

265265

266266
def test_static_append_on_full_raises():
267-
arr = StaticArray(3)
267+
arr = StaticArray(capacity=3)
268268
for i in range(3):
269269
arr.append(i)
270270
with pytest.raises(OverflowError):
271271
arr.append(99)
272272

273273

274274
def test_static_insert_on_full_raises():
275-
arr = StaticArray(3)
275+
arr = StaticArray(capacity=3)
276276
for i in range(3):
277277
arr.append(i)
278278
with pytest.raises(OverflowError):
279279
arr.insert(0, 99)
280280

281281

282282
def test_static_repr():
283-
arr = StaticArray(5)
283+
arr = StaticArray(capacity=5)
284284
arr.append(1)
285285
arr.append(2)
286286
assert repr(arr) == "StaticArray([1, 2])"
287287

288288

289289
def test_static_repr_empty():
290-
assert repr(StaticArray(5)) == "StaticArray([])"
290+
assert repr(StaticArray(capacity=5)) == "StaticArray([])"
291291

292292

293293
def test_static_str():
294-
arr = StaticArray([1, 2, 3])
294+
arr = StaticArray(1, 2, 3)
295295
assert str(arr) == "[1, 2, 3]"
296296

297297

298298
def test_static_str_empty():
299-
assert str(StaticArray(5)) == "[]"
299+
assert str(StaticArray(capacity=5)) == "[]"
300300

301301

302-
def test_static_init_from_list():
303-
arr = StaticArray([10, 20, 30])
302+
def test_static_init_from_args():
303+
arr = StaticArray(10, 20, 30)
304304
assert list(arr) == [10, 20, 30]
305305
assert arr.capacity == 3
306306
assert len(arr) == 3
307307

308308

309-
def test_static_init_from_iterable():
310-
arr = StaticArray(range(4))
311-
assert list(arr) == [0, 1, 2, 3]
312-
assert arr.capacity == 4
313-
314-
315-
def test_static_init_empty_iterable_raises():
316-
with pytest.raises(ValueError):
317-
StaticArray([])
309+
def test_static_init_no_args_raises():
310+
with pytest.raises(TypeError):
311+
StaticArray()
318312

319313

320314
# ── DynamicArray-specific tests ──────────────────────────────────────
@@ -388,32 +382,27 @@ def test_dynamic_repr_empty():
388382

389383

390384
def test_dynamic_str():
391-
arr = DynamicArray([1, 2, 3])
385+
arr = DynamicArray(1, 2, 3)
392386
assert str(arr) == "[1, 2, 3]"
393387

394388

395389
def test_dynamic_str_empty():
396390
assert str(DynamicArray()) == "[]"
397391

398392

399-
def test_dynamic_init_from_list():
400-
arr = DynamicArray([10, 20, 30])
393+
def test_dynamic_init_from_args():
394+
arr = DynamicArray(10, 20, 30)
401395
assert list(arr) == [10, 20, 30]
402396
assert len(arr) == 3
403397

404398

405-
def test_dynamic_init_from_iterable():
406-
arr = DynamicArray(range(4))
407-
assert list(arr) == [0, 1, 2, 3]
408-
409-
410-
def test_dynamic_init_empty_iterable():
411-
arr = DynamicArray([])
399+
def test_dynamic_init_empty():
400+
arr = DynamicArray()
412401
assert arr.is_empty()
413402
assert arr.capacity == 4
414403

415404

416405
def test_dynamic_init_large_sets_capacity():
417-
arr = DynamicArray(range(10))
406+
arr = DynamicArray(*range(10))
418407
assert arr.capacity >= 10
419408
assert len(arr) == 10

tests/test_benchmarks.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
def test_static_array_append(benchmark):
2323
def run():
24-
arr = StaticArray(N)
24+
arr = StaticArray(capacity=N)
2525
for i in range(N):
2626
arr.append(i)
2727

@@ -51,12 +51,12 @@ def run():
5151

5252
@pytest.fixture
5353
def static_filled():
54-
return StaticArray(range(N))
54+
return StaticArray(*range(N))
5555

5656

5757
@pytest.fixture
5858
def dynamic_filled():
59-
return DynamicArray(range(N))
59+
return DynamicArray(*range(N))
6060

6161

6262
@pytest.fixture

tests/test_binary_search.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,29 +114,29 @@ def test_upper_bound_empty():
114114

115115

116116
def test_binary_search_static_array():
117-
assert binary_search(StaticArray([1, 3, 5, 7, 9]), 5) == 2
118-
assert binary_search(StaticArray([1, 3, 5, 7, 9]), 42) == -1
117+
assert binary_search(StaticArray(1, 3, 5, 7, 9), 5) == 2
118+
assert binary_search(StaticArray(1, 3, 5, 7, 9), 42) == -1
119119

120120

121121
def test_lower_bound_static_array():
122-
assert lower_bound(StaticArray([1, 3, 5, 7]), 4) == 2
122+
assert lower_bound(StaticArray(1, 3, 5, 7), 4) == 2
123123

124124

125125
def test_upper_bound_static_array():
126-
assert upper_bound(StaticArray([1, 3, 3, 3, 5]), 3) == 4
126+
assert upper_bound(StaticArray(1, 3, 3, 3, 5), 3) == 4
127127

128128

129129
# ── DynamicArray ─────────────────────────────────────────────────────
130130

131131

132132
def test_binary_search_dynamic_array():
133-
assert binary_search(DynamicArray([1, 3, 5, 7, 9]), 5) == 2
134-
assert binary_search(DynamicArray([1, 3, 5, 7, 9]), 42) == -1
133+
assert binary_search(DynamicArray(1, 3, 5, 7, 9), 5) == 2
134+
assert binary_search(DynamicArray(1, 3, 5, 7, 9), 42) == -1
135135

136136

137137
def test_lower_bound_dynamic_array():
138-
assert lower_bound(DynamicArray([1, 3, 5, 7]), 4) == 2
138+
assert lower_bound(DynamicArray(1, 3, 5, 7), 4) == 2
139139

140140

141141
def test_upper_bound_dynamic_array():
142-
assert upper_bound(DynamicArray([1, 3, 3, 3, 5]), 3) == 4
142+
assert upper_bound(DynamicArray(1, 3, 3, 3, 5), 3) == 4

tests/test_properties.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ def test_dynamic_array_len_matches_appends(values):
3838

3939

4040
@given(ints)
41-
def test_static_array_from_iterable_roundtrips(values):
41+
def test_static_array_from_args_roundtrips(values):
4242
if not values:
4343
return
44-
arr = StaticArray(values)
44+
arr = StaticArray(*values)
4545
assert list(arr) == values
4646

4747

4848
@given(ints)
4949
def test_dynamic_array_eq_static_array(values):
5050
if not values:
5151
return
52-
da = DynamicArray(values)
53-
sa = StaticArray(values)
52+
da = DynamicArray(*values)
53+
sa = StaticArray(*values)
5454
assert da == sa
5555

5656

@@ -66,7 +66,7 @@ def test_array_pop_reverses_append(values):
6666

6767
@given(ints)
6868
def test_array_slice_matches_list_slice(values):
69-
arr = DynamicArray(values)
69+
arr = DynamicArray(*values)
7070
assert arr[:] == values
7171
assert arr[::2] == values[::2]
7272
assert arr[::-1] == values[::-1]

0 commit comments

Comments
 (0)