structures-detroix23 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- structures_detroix23-0.0.3/LICENSE.md +0 -0
- structures_detroix23-0.0.3/PKG-INFO +24 -0
- structures_detroix23-0.0.3/README.md +9 -0
- structures_detroix23-0.0.3/pyproject.toml +27 -0
- structures_detroix23-0.0.3/setup.cfg +4 -0
- structures_detroix23-0.0.3/src/structures_detroix23/__init__.py +15 -0
- structures_detroix23-0.0.3/src/structures_detroix23/__main__.py +130 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/__init__.py +4 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/base.py +18 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/cell.py +99 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/dynamic_list.py +105 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/graphs.py +56 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/nodes.py +173 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/result.py +60 -0
- structures_detroix23-0.0.3/src/structures_detroix23/modules/types.py +8 -0
- structures_detroix23-0.0.3/src/structures_detroix23.egg-info/PKG-INFO +24 -0
- structures_detroix23-0.0.3/src/structures_detroix23.egg-info/SOURCES.txt +17 -0
- structures_detroix23-0.0.3/src/structures_detroix23.egg-info/dependency_links.txt +1 -0
- structures_detroix23-0.0.3/src/structures_detroix23.egg-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: structures_detroix23
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Python structures.
|
|
5
|
+
Author-email: Detroix23 <detroix23@gmail.com>
|
|
6
|
+
License-Expression: CC-BY-4.0
|
|
7
|
+
Project-URL: Repository, https://github.com/Detroix23/py_structures
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.md
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Data structures.
|
|
17
|
+
|
|
18
|
+
Classes:
|
|
19
|
+
- Chained lists (algorithmically);
|
|
20
|
+
- Stack;
|
|
21
|
+
- Queue;
|
|
22
|
+
- Doubly-chained lists;
|
|
23
|
+
- Tree;
|
|
24
|
+
- Graphs;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "structures_detroix23"
|
|
3
|
+
version = "0.0.3"
|
|
4
|
+
description = "Python structures."
|
|
5
|
+
authors = [
|
|
6
|
+
{ name="Detroix23", email="detroix23@gmail.com" }
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Natural Language :: English",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
]
|
|
17
|
+
license = "CC-BY-4.0"
|
|
18
|
+
license-files = [
|
|
19
|
+
"LICENSE.md"
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Repository = "https://github.com/Detroix23/py_structures"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["setuptools"]
|
|
27
|
+
build-backend = "setuptools.build_meta"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/__main__.py
|
|
4
|
+
Main file.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from structures_detroix23.modules import (
|
|
10
|
+
base,
|
|
11
|
+
cell,
|
|
12
|
+
dynamic_list,
|
|
13
|
+
nodes,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def test_chain1() -> None:
|
|
17
|
+
"""
|
|
18
|
+
Main chained list test entry point.
|
|
19
|
+
"""
|
|
20
|
+
print("\n(?) main.test_chain1() Start.")
|
|
21
|
+
|
|
22
|
+
base.verbose_assert_eq(cell.Cell(1).to_list(), [1])
|
|
23
|
+
|
|
24
|
+
assert (cell.Cell(1, cell.Cell(2, cell.Cell(3)))).to_list() == [1, 2, 3]
|
|
25
|
+
|
|
26
|
+
c3: cell.Cell[int] = cell.new_from([1, 2, 3, 4])
|
|
27
|
+
assert c3.to_list() == [1, 2, 3, 4]
|
|
28
|
+
|
|
29
|
+
base.verbose_assert_eq(c3.go(0).value, 1)
|
|
30
|
+
base.verbose_assert_eq(c3.go(1).value, 2)
|
|
31
|
+
base.verbose_assert_eq(c3.go(2).value, 3)
|
|
32
|
+
base.verbose_assert_eq(c3.go(3).value, 4)
|
|
33
|
+
|
|
34
|
+
print("(?) main.test_chain1() Passed.\n")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
def test_list1() -> None:
|
|
38
|
+
print("\n(?) main.test_chain1() Start.")
|
|
39
|
+
|
|
40
|
+
c1: cell.Cell[int] = cell.new_from([3, 4, 5])
|
|
41
|
+
|
|
42
|
+
l1: dynamic_list.DynamicList[int] = dynamic_list.DynamicList()
|
|
43
|
+
l1.append(1)
|
|
44
|
+
l1.append(2)
|
|
45
|
+
l1.push(c1)
|
|
46
|
+
|
|
47
|
+
print(f"l1={l1}, len={len(l1)}, l={l1.to_list()}")
|
|
48
|
+
|
|
49
|
+
l2: dynamic_list.DynamicList[int] = dynamic_list.DynamicList()
|
|
50
|
+
print(f"l2={l2}, len={len(l2)}, l={l2.to_list()}")
|
|
51
|
+
|
|
52
|
+
l3: dynamic_list.DynamicList[int] = dynamic_list.DynamicList()
|
|
53
|
+
l3.append(1)
|
|
54
|
+
print(f"l3={l3}, len={len(l3)}, l={l3.to_list()}")
|
|
55
|
+
|
|
56
|
+
print("(?) main.test_chain1() Passed.\n")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
def test_graph1() -> None:
|
|
60
|
+
"""
|
|
61
|
+
Test nodes and graphs.
|
|
62
|
+
"""
|
|
63
|
+
n1 = nodes.Node("A")
|
|
64
|
+
n2 = nodes.Node("B")
|
|
65
|
+
n3 = nodes.Node("C")
|
|
66
|
+
n4 = nodes.Node("D")
|
|
67
|
+
|
|
68
|
+
print(n1)
|
|
69
|
+
print(n2)
|
|
70
|
+
print(n3)
|
|
71
|
+
print(n4)
|
|
72
|
+
|
|
73
|
+
n1.batch_next((
|
|
74
|
+
(n1, 1),
|
|
75
|
+
(n2, 2),
|
|
76
|
+
(n3, 2),
|
|
77
|
+
(n4, 5)
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
n1.batch_previous((
|
|
81
|
+
(n3, 4),
|
|
82
|
+
(n4, 1)
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
n4.batch_next((
|
|
86
|
+
(n2, 2),
|
|
87
|
+
(n3, 1),
|
|
88
|
+
))
|
|
89
|
+
|
|
90
|
+
print(n1, n1.display(' '))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def help() -> str:
|
|
96
|
+
return """## Help.
|
|
97
|
+
|
|
98
|
+
### Arguments.
|
|
99
|
+
`--test` | `-t`: Launch testing;
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def test() -> None:
|
|
104
|
+
print("## Tests.")
|
|
105
|
+
|
|
106
|
+
# test_chain1()
|
|
107
|
+
# test_list1()
|
|
108
|
+
|
|
109
|
+
test_graph1()
|
|
110
|
+
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
def main() -> None:
|
|
114
|
+
print("# Data structures.")
|
|
115
|
+
|
|
116
|
+
arguments: list[str] = sys.argv
|
|
117
|
+
|
|
118
|
+
if len(arguments) == 1:
|
|
119
|
+
print("\nUser selected no arguments.\n")
|
|
120
|
+
|
|
121
|
+
print(help())
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
for argument in arguments:
|
|
125
|
+
if argument in {"--test", "-t"}:
|
|
126
|
+
test()
|
|
127
|
+
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
main()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/base.py.
|
|
4
|
+
Base.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
def verbose_assert_eq(a: Any, b: Any) -> bool:
|
|
10
|
+
"""
|
|
11
|
+
Compare 2 values.
|
|
12
|
+
- raise `AssertionError` if not equal, and prints the values.
|
|
13
|
+
- returns `True` if passed.
|
|
14
|
+
"""
|
|
15
|
+
if a != b:
|
|
16
|
+
raise AssertionError(f"(X) verbose_assert_eq(a={a}, b={b}) `a` != `b`.")
|
|
17
|
+
else:
|
|
18
|
+
return True
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/chain.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional, TypeVar, Generic
|
|
7
|
+
|
|
8
|
+
_T_CELL = TypeVar("_T_CELL")
|
|
9
|
+
|
|
10
|
+
class Cell(Generic[_T_CELL]):
|
|
11
|
+
"""
|
|
12
|
+
Define a cell from a chained list.
|
|
13
|
+
"""
|
|
14
|
+
_value: _T_CELL
|
|
15
|
+
_following: Optional['Cell[_T_CELL]']
|
|
16
|
+
|
|
17
|
+
def __init__(self, value: _T_CELL, following: Optional['Cell[_T_CELL]'] = None) -> None:
|
|
18
|
+
self._value = value
|
|
19
|
+
self._following = following
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
display: list[str] = []
|
|
23
|
+
current: Optional[Cell[_T_CELL]] = self.clone()
|
|
24
|
+
while current is not None:
|
|
25
|
+
display.append(str(current.value))
|
|
26
|
+
current = current.following
|
|
27
|
+
|
|
28
|
+
return f"<{', '.join(display)}>"
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"chain.Cell(value={self.value}, following={repr(self.following)})"
|
|
32
|
+
|
|
33
|
+
def clone(self) -> 'Cell[_T_CELL]':
|
|
34
|
+
return Cell(
|
|
35
|
+
value=self.value,
|
|
36
|
+
following=self.following,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def value(self) -> _T_CELL:
|
|
41
|
+
"""
|
|
42
|
+
Get the read-only `value` of the cell.
|
|
43
|
+
"""
|
|
44
|
+
return self._value
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def following(self) -> Optional['Cell[_T_CELL]']:
|
|
48
|
+
"""
|
|
49
|
+
Get the read-only `following` next cell. Return `None` if has no following.
|
|
50
|
+
"""
|
|
51
|
+
return self._following
|
|
52
|
+
|
|
53
|
+
def set_following(self, new: 'Cell[_T_CELL]') -> None:
|
|
54
|
+
"""
|
|
55
|
+
Set the `following` to a `new` cell.
|
|
56
|
+
"""
|
|
57
|
+
print(f"(?) set_following={new}")
|
|
58
|
+
self._following = new
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def to_list(self) -> list[_T_CELL]:
|
|
62
|
+
"""
|
|
63
|
+
Returns a `list[_T_CELL]` of the values.
|
|
64
|
+
"""
|
|
65
|
+
current: Cell[_T_CELL] = self.clone()
|
|
66
|
+
l: list[_T_CELL] = [current.value]
|
|
67
|
+
|
|
68
|
+
while current.following is not None:
|
|
69
|
+
current = current.following
|
|
70
|
+
l.append(current.value)
|
|
71
|
+
|
|
72
|
+
return l
|
|
73
|
+
|
|
74
|
+
def go(self, steps: int) -> 'Cell[_T_CELL]':
|
|
75
|
+
"""
|
|
76
|
+
Move up `steps` times, and return the `Cell`.
|
|
77
|
+
Throws an `IndexError` if the index is out of range.
|
|
78
|
+
"""
|
|
79
|
+
current: Cell[_T_CELL] = self.clone()
|
|
80
|
+
|
|
81
|
+
while steps > 0:
|
|
82
|
+
if current.following is None:
|
|
83
|
+
raise IndexError(f"(X) - modules.chain.Cell.go - Index out of range. steps={steps}, cell={repr(current)}")
|
|
84
|
+
current = current.following
|
|
85
|
+
|
|
86
|
+
steps -= 1
|
|
87
|
+
|
|
88
|
+
return current
|
|
89
|
+
|
|
90
|
+
def new_from(values: list[_T_CELL]) -> 'Cell[_T_CELL]':
|
|
91
|
+
"""
|
|
92
|
+
Generate a chained list from a given `values` list.
|
|
93
|
+
"""
|
|
94
|
+
values.reverse()
|
|
95
|
+
current: Cell[_T_CELL] = Cell(values[0])
|
|
96
|
+
for value in values[1:]:
|
|
97
|
+
current = Cell(value, current)
|
|
98
|
+
|
|
99
|
+
return current
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/dynamic_list.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional, TypeVar, Generic
|
|
7
|
+
|
|
8
|
+
from structures_detroix23.modules import (
|
|
9
|
+
cell,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
_T_LIST = TypeVar("_T_LIST")
|
|
13
|
+
|
|
14
|
+
class DynamicList(Generic[_T_LIST]):
|
|
15
|
+
"""
|
|
16
|
+
# `DynamicList`.
|
|
17
|
+
"""
|
|
18
|
+
data: Optional[cell.Cell[_T_LIST]]
|
|
19
|
+
|
|
20
|
+
def __init__(self, data: Optional[cell.Cell[_T_LIST]] = None) -> None:
|
|
21
|
+
self.data = data
|
|
22
|
+
|
|
23
|
+
def __len__(self) -> int:
|
|
24
|
+
counter: int = 0
|
|
25
|
+
|
|
26
|
+
if self.data is not None:
|
|
27
|
+
current: cell.Cell[_T_LIST] = self.data
|
|
28
|
+
counter += 1
|
|
29
|
+
|
|
30
|
+
while current.following is not None:
|
|
31
|
+
current = current.following
|
|
32
|
+
counter += 1
|
|
33
|
+
|
|
34
|
+
return counter
|
|
35
|
+
|
|
36
|
+
def __repr__(self) -> str:
|
|
37
|
+
if self.is_empty():
|
|
38
|
+
return f"DynamicList(None)"
|
|
39
|
+
else:
|
|
40
|
+
return f"DynamicList({repr(self.data)})"
|
|
41
|
+
|
|
42
|
+
def __str__(self) -> str:
|
|
43
|
+
if self.is_empty():
|
|
44
|
+
return f"List<>"
|
|
45
|
+
else:
|
|
46
|
+
return f"List{self.data}"
|
|
47
|
+
|
|
48
|
+
def is_empty(self) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Return `True` if the list is empty.
|
|
51
|
+
"""
|
|
52
|
+
return self.data is None
|
|
53
|
+
|
|
54
|
+
def to_list(self) -> list[_T_LIST]:
|
|
55
|
+
if self.data is None:
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
current: cell.Cell[_T_LIST] = self.data
|
|
59
|
+
l: list[_T_LIST] = [current.value]
|
|
60
|
+
|
|
61
|
+
while current.following is not None:
|
|
62
|
+
current = current.following
|
|
63
|
+
l.append(current.value)
|
|
64
|
+
|
|
65
|
+
return l
|
|
66
|
+
|
|
67
|
+
def get_last(self) -> cell.Cell[_T_LIST]:
|
|
68
|
+
"""
|
|
69
|
+
Get a reference to the last `Cell` of the list.
|
|
70
|
+
|
|
71
|
+
Raises a `ValueError` if the list is empty.
|
|
72
|
+
"""
|
|
73
|
+
if self.data is None:
|
|
74
|
+
raise ValueError(f"(X) modules.dynamic_list.DynamicList.get_last() list ({self}) is empty.")
|
|
75
|
+
|
|
76
|
+
current: cell.Cell[_T_LIST] = self.data
|
|
77
|
+
|
|
78
|
+
while current.following is not None:
|
|
79
|
+
current = current.following
|
|
80
|
+
|
|
81
|
+
return current
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def push(self, new: cell.Cell[_T_LIST]) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Add a `Cell` to the `following` of the last current `Cell`.
|
|
87
|
+
"""
|
|
88
|
+
if self.data is None:
|
|
89
|
+
self.data = new
|
|
90
|
+
|
|
91
|
+
else:
|
|
92
|
+
last = self.get_last()
|
|
93
|
+
print(f"(?) last={last}", end=" ")
|
|
94
|
+
last.set_following(new)
|
|
95
|
+
print(f"(?) last={last}, self.get_last={self.get_last()}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def append(self, value: _T_LIST) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Add an item `value` to the end of the list.
|
|
101
|
+
|
|
102
|
+
Creates a new `Cell` englobing the `value.
|
|
103
|
+
"""
|
|
104
|
+
self.push(cell.Cell(value, None))
|
|
105
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/graphs.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from structures_detroix23.modules import (
|
|
9
|
+
nodes,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
class Graph:
|
|
13
|
+
"""
|
|
14
|
+
Describe abstractly the base of any `Graph` representation.
|
|
15
|
+
"""
|
|
16
|
+
register: list[nodes.Node]
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
register: Optional[list[nodes.Node]]
|
|
21
|
+
) -> None:
|
|
22
|
+
self.register = register if register is not None else []
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Explorer:
|
|
26
|
+
"""
|
|
27
|
+
Allows the exploring of a graph without infinite recursion.
|
|
28
|
+
"""
|
|
29
|
+
graph: Graph
|
|
30
|
+
current: nodes.Node
|
|
31
|
+
seen: set[nodes.Node]
|
|
32
|
+
|
|
33
|
+
def __init__(self, graph: Graph, current: nodes.Node) -> None:
|
|
34
|
+
self.graph = graph
|
|
35
|
+
self.current = current
|
|
36
|
+
self.seen = set()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def move(self, destination: nodes.Node) -> bool:
|
|
40
|
+
"""
|
|
41
|
+
Move to a `Node`. Return `True` if succeed, `False` if:
|
|
42
|
+
- is not reachable from `current`,
|
|
43
|
+
- has already been `seen`.
|
|
44
|
+
"""
|
|
45
|
+
if (
|
|
46
|
+
destination in self.current.get_next().keys()
|
|
47
|
+
and destination not in self.seen
|
|
48
|
+
):
|
|
49
|
+
self.current = destination
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/nodes.py.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional, Self, Iterable
|
|
7
|
+
|
|
8
|
+
from structures_detroix23.modules.types import Weight
|
|
9
|
+
|
|
10
|
+
class Node:
|
|
11
|
+
"""
|
|
12
|
+
# `Node`.
|
|
13
|
+
Define a named cell, holding no value except:
|
|
14
|
+
- `_name`: `str`, a globally unique name.
|
|
15
|
+
- `_previous`: `dict['Node', Weight]`, containing as many references to other nodes.
|
|
16
|
+
- `_next`: `dict['Node', Weight]`, containing as many references to other nodes.
|
|
17
|
+
|
|
18
|
+
Class variables:
|
|
19
|
+
- `last_id`: `int`, store the last created id, to ensure uniqueness.
|
|
20
|
+
"""
|
|
21
|
+
_last_id: int = 0
|
|
22
|
+
|
|
23
|
+
_id: int
|
|
24
|
+
_name: str
|
|
25
|
+
_previous: dict['Node', Weight]
|
|
26
|
+
_next: dict['Node', Weight]
|
|
27
|
+
|
|
28
|
+
def __new__(
|
|
29
|
+
cls,
|
|
30
|
+
name: str,
|
|
31
|
+
previous: Optional[dict['Node', Weight]] = None,
|
|
32
|
+
next: Optional[dict['Node', Weight]] = None,
|
|
33
|
+
) -> Self:
|
|
34
|
+
new: Self = super(Node, cls).__new__(cls)
|
|
35
|
+
|
|
36
|
+
new._id = Node._last_id
|
|
37
|
+
Node._last_id += 1
|
|
38
|
+
|
|
39
|
+
return new
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
name: str,
|
|
44
|
+
previous: Optional[dict['Node', Weight]] = None,
|
|
45
|
+
next: Optional[dict['Node', Weight]] = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Instantiate a new `Node`.
|
|
49
|
+
- `name` must be unique globally.
|
|
50
|
+
"""
|
|
51
|
+
self._name = name
|
|
52
|
+
self._previous = previous if previous is not None else {}
|
|
53
|
+
self._next = next if next is not None else {}
|
|
54
|
+
|
|
55
|
+
def __hash__(self) -> int:
|
|
56
|
+
return self.get_id()
|
|
57
|
+
|
|
58
|
+
def __repr__(self) -> str:
|
|
59
|
+
return f"Node(name={self._name}, id={self._id}, previous={self._previous}, next={self._next})"
|
|
60
|
+
|
|
61
|
+
def __str__(self) -> str:
|
|
62
|
+
return f"{self._name}"
|
|
63
|
+
|
|
64
|
+
def get_id(self) -> int:
|
|
65
|
+
"""
|
|
66
|
+
Returns the unique, read-only `_id` of the `Node`.
|
|
67
|
+
"""
|
|
68
|
+
return self._id
|
|
69
|
+
|
|
70
|
+
def get_name(self) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Returns the read-only `_name` of the `Node`.
|
|
73
|
+
"""
|
|
74
|
+
return self._name
|
|
75
|
+
|
|
76
|
+
def get_previous(self) -> dict['Node', Weight]:
|
|
77
|
+
"""
|
|
78
|
+
Returns the read-only `_previous` of the `Node`.
|
|
79
|
+
"""
|
|
80
|
+
return self._previous
|
|
81
|
+
|
|
82
|
+
def get_next(self) -> dict['Node', Weight]:
|
|
83
|
+
"""
|
|
84
|
+
Returns the read-only `_next` of the `Node`.
|
|
85
|
+
"""
|
|
86
|
+
return self._next
|
|
87
|
+
|
|
88
|
+
def set_previous(self, node: 'Node', weight: Weight) -> None:
|
|
89
|
+
"""
|
|
90
|
+
Create or update in `previous` the `node` with `weight`.
|
|
91
|
+
"""
|
|
92
|
+
self._previous[node] = weight
|
|
93
|
+
|
|
94
|
+
def set_next(self, node: 'Node', weight: Weight) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Create or update in `next` the `node` with `weight`.
|
|
97
|
+
"""
|
|
98
|
+
self._next[node] = weight
|
|
99
|
+
|
|
100
|
+
def batch_previous(self, nodes: Iterable[tuple['Node', Weight]]) -> None:
|
|
101
|
+
"""
|
|
102
|
+
Update multiple `nodes` in `previous`, from an `Iterable` of `(Node, Weight)`.
|
|
103
|
+
"""
|
|
104
|
+
for node, weight in nodes:
|
|
105
|
+
self.set_previous(node, weight)
|
|
106
|
+
|
|
107
|
+
def batch_next(self, nodes: Iterable[tuple['Node', Weight]]) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Update multiple `nodes` in `next`, from an `Iterable` of `(Node, Weight)`.
|
|
110
|
+
"""
|
|
111
|
+
for node, weight in nodes:
|
|
112
|
+
self.set_next(node, weight)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def remove_previous(self, node: 'Node') -> Optional[Weight]:
|
|
116
|
+
"""
|
|
117
|
+
Remove the given `node` from `previous`.
|
|
118
|
+
|
|
119
|
+
Return the weight, or `None` if `node` didn't existed.
|
|
120
|
+
"""
|
|
121
|
+
self._previous.pop(node, None)
|
|
122
|
+
|
|
123
|
+
def remove_next(self, node: 'Node') -> Optional[Weight]:
|
|
124
|
+
"""
|
|
125
|
+
Remove the given `node` from `next`.
|
|
126
|
+
|
|
127
|
+
Return the weight, or `None` if `node` didn't existed.
|
|
128
|
+
"""
|
|
129
|
+
self._next.pop(node, None)
|
|
130
|
+
|
|
131
|
+
def display(
|
|
132
|
+
self,
|
|
133
|
+
tab: str,
|
|
134
|
+
level: int = 0,
|
|
135
|
+
seen: Optional[set['Node']] = None,
|
|
136
|
+
) -> str:
|
|
137
|
+
"""
|
|
138
|
+
Multiple line readable representation of the `Node`.
|
|
139
|
+
"""
|
|
140
|
+
seen_current: set[Node] = seen if seen is not None else set[Node]()
|
|
141
|
+
|
|
142
|
+
return "\n".join([
|
|
143
|
+
"{",
|
|
144
|
+
f"{tab * (level + 1)}previous: {pretty(self.get_previous(), seen_current, tab, level + 1)},",
|
|
145
|
+
f"{tab * (level + 1)}next: {pretty(self.get_next(), seen_current, tab, level + 1)},",
|
|
146
|
+
f"{tab * level}" + "}"
|
|
147
|
+
])
|
|
148
|
+
|
|
149
|
+
def pretty(
|
|
150
|
+
iterable: dict['Node', Weight],
|
|
151
|
+
seen: set['Node'],
|
|
152
|
+
tab: str = "\t",
|
|
153
|
+
level: int = 0,
|
|
154
|
+
) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Return a pretty formatted line broken string of a `dict`.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
lines: list[str] = ["{"]
|
|
160
|
+
|
|
161
|
+
if len(iterable) == 0:
|
|
162
|
+
return "{}"
|
|
163
|
+
|
|
164
|
+
for node, weight in iterable.items():
|
|
165
|
+
if node in seen:
|
|
166
|
+
lines.append(f"{tab * (level + 1)}{node}: {weight}")
|
|
167
|
+
else:
|
|
168
|
+
seen.add(node)
|
|
169
|
+
lines.append(f"{tab * (level + 1)}{node}: {weight}, {node.display(tab, level + 1, seen)}")
|
|
170
|
+
|
|
171
|
+
lines.append(tab * level + "}")
|
|
172
|
+
|
|
173
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Data structures.
|
|
3
|
+
/src/structures_detroix23/modules/nodes.py.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Optional, TypeVar, Generic
|
|
7
|
+
|
|
8
|
+
_T_RESULT_ERROR = TypeVar("_T_RESULT_ERROR")
|
|
9
|
+
_T_RESULT_OK = TypeVar("_T_RESULT_OK")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UnwrapException(Exception):
|
|
13
|
+
"""
|
|
14
|
+
# `UnwrapException`.
|
|
15
|
+
Raised when trying to unwrap a `Result` containing an error.
|
|
16
|
+
"""
|
|
17
|
+
message: str
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: str) -> None:
|
|
20
|
+
super().__init__(message)
|
|
21
|
+
self.message = message
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return super().__str__()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Result(Generic[_T_RESULT_OK, _T_RESULT_ERROR]):
|
|
28
|
+
"""
|
|
29
|
+
# `Result`.
|
|
30
|
+
Rust like `Result`, containing or an error, or a success value.
|
|
31
|
+
|
|
32
|
+
Define 2 generics:
|
|
33
|
+
1. `_T_RESULT_OK`,
|
|
34
|
+
2. `_T_RESULT_ERROR`.
|
|
35
|
+
"""
|
|
36
|
+
ok: Optional[_T_RESULT_OK]
|
|
37
|
+
error: Optional[_T_RESULT_ERROR]
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
ok: Optional[_T_RESULT_OK],
|
|
42
|
+
error: Optional[_T_RESULT_ERROR]
|
|
43
|
+
) -> None:
|
|
44
|
+
self.ok = ok
|
|
45
|
+
self.error = error
|
|
46
|
+
|
|
47
|
+
def unwrap(self) -> _T_RESULT_OK:
|
|
48
|
+
"""
|
|
49
|
+
Return the `ok` value. If there is an `error, raise an `UnwrapException`.
|
|
50
|
+
"""
|
|
51
|
+
if self.error is not None or self.ok is None:
|
|
52
|
+
raise UnwrapException(str(self.error))
|
|
53
|
+
|
|
54
|
+
return self.ok
|
|
55
|
+
|
|
56
|
+
def is_ok(self) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Return `True` if no error.
|
|
59
|
+
"""
|
|
60
|
+
return self.error is None and self.ok is not None
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: structures_detroix23
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: Python structures.
|
|
5
|
+
Author-email: Detroix23 <detroix23@gmail.com>
|
|
6
|
+
License-Expression: CC-BY-4.0
|
|
7
|
+
Project-URL: Repository, https://github.com/Detroix23/py_structures
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Natural Language :: English
|
|
11
|
+
Requires-Python: >=3.12
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.md
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Data structures.
|
|
17
|
+
|
|
18
|
+
Classes:
|
|
19
|
+
- Chained lists (algorithmically);
|
|
20
|
+
- Stack;
|
|
21
|
+
- Queue;
|
|
22
|
+
- Doubly-chained lists;
|
|
23
|
+
- Tree;
|
|
24
|
+
- Graphs;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/structures_detroix23/__init__.py
|
|
5
|
+
src/structures_detroix23/__main__.py
|
|
6
|
+
src/structures_detroix23.egg-info/PKG-INFO
|
|
7
|
+
src/structures_detroix23.egg-info/SOURCES.txt
|
|
8
|
+
src/structures_detroix23.egg-info/dependency_links.txt
|
|
9
|
+
src/structures_detroix23.egg-info/top_level.txt
|
|
10
|
+
src/structures_detroix23/modules/__init__.py
|
|
11
|
+
src/structures_detroix23/modules/base.py
|
|
12
|
+
src/structures_detroix23/modules/cell.py
|
|
13
|
+
src/structures_detroix23/modules/dynamic_list.py
|
|
14
|
+
src/structures_detroix23/modules/graphs.py
|
|
15
|
+
src/structures_detroix23/modules/nodes.py
|
|
16
|
+
src/structures_detroix23/modules/result.py
|
|
17
|
+
src/structures_detroix23/modules/types.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
structures_detroix23
|