danielutils 1.1.28__py3-none-any.whl
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.
- danielutils/__init__.py +50 -0
- danielutils/abstractions/__init__.py +3 -0
- danielutils/abstractions/db/__init__.py +10 -0
- danielutils/abstractions/db/database.py +206 -0
- danielutils/abstractions/db/database_definitions.py +219 -0
- danielutils/abstractions/db/database_exceptions.py +34 -0
- danielutils/abstractions/db/database_factory.py +70 -0
- danielutils/abstractions/db/database_initializer.py +261 -0
- danielutils/abstractions/db/database_models.py +213 -0
- danielutils/abstractions/db/dependencies.py +6 -0
- danielutils/abstractions/db/implementations/__init__.py +4 -0
- danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
- danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
- danielutils/abstractions/db/implementations/redis_database.py +403 -0
- danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
- danielutils/abstractions/multiprogramming/__init__.py +3 -0
- danielutils/abstractions/multiprogramming/multi_id.py +26 -0
- danielutils/abstractions/multiprogramming/worker.py +104 -0
- danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
- danielutils/abstractions/repl.py +125 -0
- danielutils/aliases.py +174 -0
- danielutils/async_/__init__.py +7 -0
- danielutils/async_/async_cmd.py +28 -0
- danielutils/async_/async_command.py +879 -0
- danielutils/async_/async_layered_command.py +125 -0
- danielutils/async_/async_retry_executor.py +86 -0
- danielutils/async_/async_worker_pool.py +110 -0
- danielutils/async_/time_strategy.py +73 -0
- danielutils/async_/utils.py +57 -0
- danielutils/better_builtins/__init__.py +3 -0
- danielutils/better_builtins/counter.py +48 -0
- danielutils/better_builtins/frange.py +266 -0
- danielutils/better_builtins/typed_builtins/__init__.py +4 -0
- danielutils/better_builtins/typed_builtins/factory.py +126 -0
- danielutils/better_builtins/typed_builtins/tdict.py +36 -0
- danielutils/better_builtins/typed_builtins/tlist.py +223 -0
- danielutils/better_builtins/typed_builtins/tset.py +67 -0
- danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
- danielutils/colors.py +203 -0
- danielutils/context_managers/__init__.py +5 -0
- danielutils/context_managers/attr_context.py +40 -0
- danielutils/context_managers/multi_context.py +34 -0
- danielutils/context_managers/optional_context.py +33 -0
- danielutils/context_managers/state_context.py +25 -0
- danielutils/context_managers/temporary_file.py +108 -0
- danielutils/convenience.py +12 -0
- danielutils/conversions/__init__.py +2 -0
- danielutils/conversions/main_conversions.py +131 -0
- danielutils/conversions/specialized_conversions/__init__.py +2 -0
- danielutils/conversions/specialized_conversions/to_hex.py +25 -0
- danielutils/conversions/specialized_conversions/to_int.py +19 -0
- danielutils/custom_types.py +18 -0
- danielutils/data_structures/__init__.py +8 -0
- danielutils/data_structures/algorithms.py +52 -0
- danielutils/data_structures/comparer.py +47 -0
- danielutils/data_structures/default_dict.py +23 -0
- danielutils/data_structures/functions.py +35 -0
- danielutils/data_structures/graph/__init__.py +4 -0
- danielutils/data_structures/graph/binary_node.py +71 -0
- danielutils/data_structures/graph/graph.py +208 -0
- danielutils/data_structures/graph/multinode.py +101 -0
- danielutils/data_structures/graph/node.py +60 -0
- danielutils/data_structures/heap/__init__.py +3 -0
- danielutils/data_structures/heap/heap.py +119 -0
- danielutils/data_structures/heap/max_heap.py +18 -0
- danielutils/data_structures/heap/min_heap.py +18 -0
- danielutils/data_structures/queue/__init__.py +3 -0
- danielutils/data_structures/queue/atomic_queue.py +15 -0
- danielutils/data_structures/queue/priority_queue.py +110 -0
- danielutils/data_structures/queue/queue.py +92 -0
- danielutils/data_structures/stack.py +102 -0
- danielutils/data_structures/trees/__init__.py +2 -0
- danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
- danielutils/data_structures/trees/binary_tree.py +63 -0
- danielutils/date.py +43 -0
- danielutils/date_time.py +23 -0
- danielutils/decorators/__init__.py +18 -0
- danielutils/decorators/atomic.py +45 -0
- danielutils/decorators/attach.py +61 -0
- danielutils/decorators/chain_decorators.py +39 -0
- danielutils/decorators/decorate_conditionally.py +32 -0
- danielutils/decorators/delay_call.py +47 -0
- danielutils/decorators/deprecate.py +46 -0
- danielutils/decorators/final.py +39 -0
- danielutils/decorators/limit_recursion.py +63 -0
- danielutils/decorators/memo.py +68 -0
- danielutils/decorators/normalize_decorator.py +55 -0
- danielutils/decorators/overload.py +204 -0
- danielutils/decorators/partially_implemented.py +32 -0
- danielutils/decorators/processify.py +74 -0
- danielutils/decorators/property.py +42 -0
- danielutils/decorators/singleton.py +59 -0
- danielutils/decorators/threadify.py +40 -0
- danielutils/decorators/timeout.py +82 -0
- danielutils/decorators/total_ordering.py +82 -0
- danielutils/decorators/validate.py +227 -0
- danielutils/exceptions.py +90 -0
- danielutils/functions/__init__.py +13 -0
- danielutils/functions/areoneof.py +47 -0
- danielutils/functions/check_foreach.py +39 -0
- danielutils/functions/factorial.py +24 -0
- danielutils/functions/flatten.py +33 -0
- danielutils/functions/foreach.py +20 -0
- danielutils/functions/isoftype.py +475 -0
- danielutils/functions/isoneof.py +71 -0
- danielutils/functions/multiloop.py +51 -0
- danielutils/functions/parallel_for.py +47 -0
- danielutils/functions/partition.py +45 -0
- danielutils/functions/powerset.py +31 -0
- danielutils/functions/subseteq.py +25 -0
- danielutils/functions/types_subseteq.py +51 -0
- danielutils/generators/__init__.py +3 -0
- danielutils/generators/conditional_generator.py +59 -0
- danielutils/generators/generator_from_stream.py +31 -0
- danielutils/generators/join_generators.py +109 -0
- danielutils/internet.py +92 -0
- danielutils/io_.py +451 -0
- danielutils/java/__init__.py +18 -0
- danielutils/java/interfaces/__init__.py +1 -0
- danielutils/java/interfaces/comparable.py +31 -0
- danielutils/java/java_interface.py +59 -0
- danielutils/logging_/__init__.py +2 -0
- danielutils/logging_/_impl/__init__.py +4 -0
- danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
- danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
- danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
- danielutils/logging_/_impl/log_level.py +13 -0
- danielutils/logging_/_impl/logger.py +109 -0
- danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
- danielutils/logging_/utils.py +199 -0
- danielutils/lombok/__init__.py +1 -0
- danielutils/lombok/builder.py +68 -0
- danielutils/math_/__init__.py +2 -0
- danielutils/math_/constants.py +103 -0
- danielutils/math_/functions.py +34 -0
- danielutils/math_/math_print.py +52 -0
- danielutils/math_/math_symbols.py +125 -0
- danielutils/metaclasses/__init__.py +5 -0
- danielutils/metaclasses/atomic_class_meta.py +36 -0
- danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
- danielutils/metaclasses/instance_cache_meta.py +66 -0
- danielutils/metaclasses/interface.py +328 -0
- danielutils/metaclasses/overload_meta.py +48 -0
- danielutils/mock_/__init__.py +1 -0
- danielutils/mock_/mock_module.py +27 -0
- danielutils/path.py +55 -0
- danielutils/print_.py +123 -0
- danielutils/progress_bar/__init__.py +3 -0
- danielutils/progress_bar/ascii_progress_bar.py +161 -0
- danielutils/progress_bar/progress_bar.py +75 -0
- danielutils/progress_bar/progress_bar_pool.py +72 -0
- danielutils/protocols/__init__.py +3 -0
- danielutils/protocols/dictable.py +38 -0
- danielutils/protocols/evaluable.py +26 -0
- danielutils/protocols/serializable.py +51 -0
- danielutils/py.typed +0 -0
- danielutils/random_.py +47 -0
- danielutils/reflection/__init__.py +6 -0
- danielutils/reflection/declarations.py +288 -0
- danielutils/reflection/file/__init__.py +1 -0
- danielutils/reflection/file/file_reflection.py +85 -0
- danielutils/reflection/function/__init__.py +1 -0
- danielutils/reflection/function/function_reflections.py +152 -0
- danielutils/reflection/info_classes/__init__.py +6 -0
- danielutils/reflection/info_classes/argument_info.py +278 -0
- danielutils/reflection/info_classes/class_info.py +204 -0
- danielutils/reflection/info_classes/decorator_info.py +54 -0
- danielutils/reflection/info_classes/file_info.py +1180 -0
- danielutils/reflection/info_classes/folder_info.py +592 -0
- danielutils/reflection/info_classes/function_info.py +551 -0
- danielutils/reflection/info_classes/import_info.py +207 -0
- danielutils/reflection/interpreter/__init__.py +9 -0
- danielutils/reflection/interpreter/callstack.py +57 -0
- danielutils/reflection/interpreter/get_traceback.py +27 -0
- danielutils/reflection/interpreter/interpreter.py +41 -0
- danielutils/reflection/interpreter/is_debugging.py +13 -0
- danielutils/reflection/interpreter/os_.py +33 -0
- danielutils/reflection/interpreter/packages.py +33 -0
- danielutils/reflection/interpreter/python_version.py +33 -0
- danielutils/reflection/interpreter/signals.py +18 -0
- danielutils/reflection/interpreter/tracer.py +175 -0
- danielutils/reflection/module/__init__.py +3 -0
- danielutils/reflection/module/lazy_module.py +104 -0
- danielutils/reflection/module/module_reflections.py +34 -0
- danielutils/reflection/module/package_reflection.py +161 -0
- danielutils/retry_executor/__init__.py +3 -0
- danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
- danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
- danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
- danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
- danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
- danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
- danielutils/retry_executor/backoff_strategy.py +28 -0
- danielutils/retry_executor/retry_executor.py +55 -0
- danielutils/snippets/__init__.py +1 -0
- danielutils/snippets/try_get.py +28 -0
- danielutils/system/__init__.py +3 -0
- danielutils/system/independent.py +197 -0
- danielutils/system/layered_command.py +130 -0
- danielutils/system/windows/__init__.py +2 -0
- danielutils/system/windows/utils/__init__.py +1 -0
- danielutils/system/windows/utils/filetime.py +19 -0
- danielutils/system/windows/win32_ctime.py +183 -0
- danielutils/system/windows/windows.py +78 -0
- danielutils/testing/__init__.py +1 -0
- danielutils/testing/unittest_/__init__.py +2 -0
- danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
- danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
- danielutils/text.py +169 -0
- danielutils/time.py +64 -0
- danielutils/tools/__init__.py +1 -0
- danielutils/tools/unittest_test_runner/__init__.py +9 -0
- danielutils/tools/unittest_test_runner/cli.py +70 -0
- danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
- danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
- danielutils/tools/unittest_test_runner/core/execution.py +244 -0
- danielutils/tools/unittest_test_runner/core/parser.py +163 -0
- danielutils/tools/unittest_test_runner/core/runner.py +561 -0
- danielutils/tools/unittest_test_runner/models.py +82 -0
- danielutils/university/__init__.py +7 -0
- danielutils/university/computability_and_complexity/__init__.py +2 -0
- danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
- danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
- danielutils/university/computability_and_complexity/languages/language.py +17 -0
- danielutils/university/computability_and_complexity/languages/sat.py +71 -0
- danielutils/university/computability_and_complexity/turing_machine.py +47 -0
- danielutils/university/databases/__init__.py +1 -0
- danielutils/university/databases/all.py +756 -0
- danielutils/university/image_proccesing/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/__init__.py +3 -0
- danielutils/university/image_proccesing/encoding/encoding.py +35 -0
- danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
- danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
- danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
- danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
- danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
- danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
- danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
- danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
- danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
- danielutils/university/image_proccesing/tansformations/hough.py +0 -0
- danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
- danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
- danielutils/university/linear_algebra/__init__.py +1 -0
- danielutils/university/linear_algebra/matrix.py +404 -0
- danielutils/university/machine_learning/__init__.py +0 -0
- danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
- danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
- danielutils/university/machine_learning/activation_functions/relu.py +11 -0
- danielutils/university/machine_learning/neuron.py +15 -0
- danielutils/university/oop/__init__.py +1 -0
- danielutils/university/oop/observer.py +31 -0
- danielutils/university/oop/strategy.py +9 -0
- danielutils/university/probability/__init__.py +7 -0
- danielutils/university/probability/conditional_variable/__init__.py +3 -0
- danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
- danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
- danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
- danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
- danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
- danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
- danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
- danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
- danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
- danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
- danielutils/university/probability/distributions.py +35 -0
- danielutils/university/probability/expressions/__init__.py +2 -0
- danielutils/university/probability/expressions/accumulation_expression.py +291 -0
- danielutils/university/probability/expressions/probability_expression.py +110 -0
- danielutils/university/probability/funcs/__init__.py +4 -0
- danielutils/university/probability/funcs/covariance.py +12 -0
- danielutils/university/probability/funcs/expected_value.py +41 -0
- danielutils/university/probability/funcs/probability_function.py +14 -0
- danielutils/university/probability/funcs/variance.py +17 -0
- danielutils/university/probability/operator.py +79 -0
- danielutils/university/probability/protocols.py +32 -0
- danielutils/university/probability/supp.py +101 -0
- danielutils/university/probability/transformation.py +44 -0
- danielutils/versioned_imports.py +31 -0
- danielutils-1.1.28.dist-info/METADATA +390 -0
- danielutils-1.1.28.dist-info/RECORD +287 -0
- danielutils-1.1.28.dist-info/WHEEL +5 -0
- danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
- danielutils-1.1.28.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from ...metaclasses.overload_meta import OverloadMeta
|
|
2
|
+
from ..main_conversions import int_to_hex, char_to_hex
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@OverloadMeta.overload
|
|
6
|
+
def to_hex(v: int) -> str:
|
|
7
|
+
# docstring at last implementation
|
|
8
|
+
return int_to_hex(v)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@to_hex.overload # type:ignore
|
|
12
|
+
def to_hex2(v: str) -> str:
|
|
13
|
+
"""to_hex has several options:\n
|
|
14
|
+
1. type(v) == int\n
|
|
15
|
+
2. type(v) == str and len(v) == 1
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: str of the hex value
|
|
19
|
+
"""
|
|
20
|
+
return char_to_hex(v)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"to_hex"
|
|
25
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""functions that convert values to int"""
|
|
2
|
+
from typing import Union, List as List
|
|
3
|
+
from ..main_conversions import char_to_int
|
|
4
|
+
from ...reflection import get_python_version
|
|
5
|
+
if get_python_version() >= (3, 9):
|
|
6
|
+
from builtins import list as List
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def to_int(value: str) -> Union[int, List[int]]:
|
|
10
|
+
"""converts a single character or a full string to an int or list of int respectively
|
|
11
|
+
"""
|
|
12
|
+
if len(value) == 1:
|
|
13
|
+
return char_to_int(value)
|
|
14
|
+
return [char_to_int(ch) for ch in value]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"to_int"
|
|
19
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Callable, TypeVar
|
|
2
|
+
from .versioned_imports import TypeAlias
|
|
3
|
+
|
|
4
|
+
T = TypeVar('T')
|
|
5
|
+
U = TypeVar('U')
|
|
6
|
+
Procedure: TypeAlias = Callable[[], None]
|
|
7
|
+
Supplier: TypeAlias = Callable[[], T]
|
|
8
|
+
Consumer: TypeAlias = Callable[[T], None]
|
|
9
|
+
Predicate: TypeAlias = Callable[[], bool]
|
|
10
|
+
Mapper: TypeAlias = Callable[[T], U]
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
'Procedure',
|
|
14
|
+
'Supplier',
|
|
15
|
+
'Consumer',
|
|
16
|
+
'Predicate',
|
|
17
|
+
'Mapper'
|
|
18
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from typing import TypeVar, List, Callable, Dict
|
|
4
|
+
from itertools import product
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from ..logging_.utils import get_logger
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
NodeT = TypeVar("NodeT")
|
|
10
|
+
|
|
11
|
+
DistanceMatrix = Dict[NodeT, Dict[NodeT, float]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def bellman_ford(nodes: List[NodeT], weight_func: Callable[[NodeT, NodeT], float],
|
|
15
|
+
iteration_callback: Callable[[DistanceMatrix], None],
|
|
16
|
+
poisoned_reverse: bool = False) -> DistanceMatrix:
|
|
17
|
+
logger.debug("Starting Bellman-Ford algorithm with %s nodes, poisoned_reverse=%s", len(nodes), poisoned_reverse)
|
|
18
|
+
dist: Dict[NodeT, Dict[NodeT, float]] = defaultdict(defaultdict)
|
|
19
|
+
prev: Dict[NodeT, Dict[NodeT, NodeT]] = defaultdict(defaultdict)
|
|
20
|
+
|
|
21
|
+
for u, v in product(nodes, nodes):
|
|
22
|
+
dist[u][v] = weight_func(u, v)
|
|
23
|
+
|
|
24
|
+
logger.debug("Initial distance matrix computed")
|
|
25
|
+
iteration_callback(dist)
|
|
26
|
+
|
|
27
|
+
for iteration in range(len(nodes) - 1):
|
|
28
|
+
logger.debug("Bellman-Ford iteration %s/%s", iteration + 1, len(nodes) - 1)
|
|
29
|
+
tmp = deepcopy(dist)
|
|
30
|
+
for u, v in product(nodes, nodes):
|
|
31
|
+
if u == v:
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
for mid in nodes:
|
|
35
|
+
if mid == u or mid == v:
|
|
36
|
+
continue
|
|
37
|
+
|
|
38
|
+
if dist[u][v] > dist[u][mid] + dist[mid][v]:
|
|
39
|
+
tmp[u][v] = dist[u][mid] + dist[mid][v]
|
|
40
|
+
prev[u][v] = mid
|
|
41
|
+
|
|
42
|
+
dist = tmp
|
|
43
|
+
iteration_callback(dist)
|
|
44
|
+
|
|
45
|
+
logger.debug("Bellman-Ford algorithm completed")
|
|
46
|
+
iteration_callback(prev)
|
|
47
|
+
return dist
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"bellman_ford"
|
|
52
|
+
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Comparer class"""
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Callable, Union, Generic, TypeVar
|
|
4
|
+
from ..logging_.utils import get_logger
|
|
5
|
+
from .functions import default_weight_function
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
U = TypeVar("U")
|
|
9
|
+
V = TypeVar("V")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Comparer(Generic[U, V]):
|
|
13
|
+
"""a Comparer class to be used when comparing two objects
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, func: Callable[[U, V], Union[int, float]]):
|
|
17
|
+
self.func = func
|
|
18
|
+
logger.debug("Comparer initialized with function: %s", func.__name__)
|
|
19
|
+
|
|
20
|
+
def compare(self, v1: U, v2: V) -> Union[int, float]:
|
|
21
|
+
"""compares two objects
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
v1 (Any): first object
|
|
25
|
+
v2 (Any): second object
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
int: a number specifying the order of the objects
|
|
29
|
+
"""
|
|
30
|
+
logger.debug("Comparing objects: %s vs %s", v1, v2)
|
|
31
|
+
result = self.func(v1, v2)
|
|
32
|
+
logger.debug("Comparison result: %s", result)
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
def __call__(self, v1: U, v2: V) -> Union[int, float]:
|
|
36
|
+
return self.compare(v1, v2)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
CompareGreater: Comparer[U, V] = Comparer(lambda a, b: default_weight_function(a) -
|
|
40
|
+
default_weight_function(b))
|
|
41
|
+
CompareSmaller: Comparer[U, V] = Comparer(lambda a, b: default_weight_function(b) -
|
|
42
|
+
default_weight_function(a))
|
|
43
|
+
__all__ = [
|
|
44
|
+
"Comparer",
|
|
45
|
+
"CompareGreater",
|
|
46
|
+
"CompareSmaller"
|
|
47
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from ..logging_.utils import get_logger
|
|
3
|
+
logger = get_logger(__name__)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DefaultDict(dict):
|
|
7
|
+
"""
|
|
8
|
+
My implementation to `collections.defaultdict`
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, cls):
|
|
12
|
+
self._cls = cls
|
|
13
|
+
logger.debug("DefaultDict initialized with default factory: %s", cls)
|
|
14
|
+
|
|
15
|
+
def __getitem__(self, key):
|
|
16
|
+
if key not in self:
|
|
17
|
+
logger.debug("Key '%s' not found, creating default value using %s", key, self._cls)
|
|
18
|
+
return super().get(key, self._cls())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"DefaultDict"
|
|
23
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any, Union
|
|
3
|
+
from ..functions import isoftype
|
|
4
|
+
from ..logging_.utils import get_logger
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def default_weight_function(v: Any) -> Union[int, float]:
|
|
9
|
+
"""will return the weight of an object
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
v (Any): object
|
|
13
|
+
|
|
14
|
+
Raises:
|
|
15
|
+
AttributeError: if the object is not a number or doesn't have __weight__ function defined
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
Union[int, float]: the object's weight
|
|
19
|
+
"""
|
|
20
|
+
logger.debug("Computing weight for object: %s", v)
|
|
21
|
+
if isoftype(v, Union[int, float]): # type:ignore
|
|
22
|
+
logger.debug("Object is numeric, returning value: %s", v)
|
|
23
|
+
return v
|
|
24
|
+
if hasattr(v, "__weight__"):
|
|
25
|
+
logger.debug("Object has __weight__ method, calling it")
|
|
26
|
+
result = v.__weight__()
|
|
27
|
+
logger.debug("Weight method returned: %s", result)
|
|
28
|
+
return result
|
|
29
|
+
logger.error("Object %s has no __weight__ function and is not numeric", v)
|
|
30
|
+
raise AttributeError(f"{v} has no __weight__ function")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"default_weight_function"
|
|
35
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional, TypeVar
|
|
3
|
+
from .multinode import MultiNode
|
|
4
|
+
from ...metaclasses import ImplicitDataDeleterMeta
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BinaryNode(MultiNode[T], metaclass=ImplicitDataDeleterMeta):
|
|
10
|
+
"""A 'classic' node class with only one child
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, data: T, l: Optional[BinaryNode[T]] = None,
|
|
14
|
+
r: Optional[BinaryNode[T]] = None): # pylint: disable=redefined-builtin
|
|
15
|
+
# intentionally can be None
|
|
16
|
+
super().__init__(data, [l, r]) # type:ignore
|
|
17
|
+
|
|
18
|
+
def __str__(self):
|
|
19
|
+
return MultiNode.__str__(self).replace(
|
|
20
|
+
self.__class__.__mro__[1].__name__,
|
|
21
|
+
self.__class__.__name__
|
|
22
|
+
).replace("[", "").replace("]", "")
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return str(self)
|
|
26
|
+
|
|
27
|
+
def __reversed__(self) -> 'BinaryNode[T]':
|
|
28
|
+
return self.reverse()
|
|
29
|
+
|
|
30
|
+
def __eq__(self, other):
|
|
31
|
+
return MultiNode.__eq__(self, other)
|
|
32
|
+
|
|
33
|
+
def __iter__(self):
|
|
34
|
+
return MultiNode.__iter__(self)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def left(self) -> "BinaryNode[T]":
|
|
38
|
+
"""return the next node after self
|
|
39
|
+
"""
|
|
40
|
+
return self._children[0] # type:ignore
|
|
41
|
+
|
|
42
|
+
@left.setter
|
|
43
|
+
def left(self, value: "BinaryNode[T]") -> None:
|
|
44
|
+
self._children[0] = value
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def right(self) -> "BinaryNode[T]":
|
|
48
|
+
"""return the next node after self
|
|
49
|
+
"""
|
|
50
|
+
return self._children[1] # type:ignore
|
|
51
|
+
|
|
52
|
+
@right.setter
|
|
53
|
+
def right(self, value: "BinaryNode[T]") -> None:
|
|
54
|
+
self._children[1] = value
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def is_leaf(self) -> bool:
|
|
58
|
+
return self.left is None and self.right is None
|
|
59
|
+
|
|
60
|
+
def reverse(self) -> 'BinaryNode[T]':
|
|
61
|
+
new_left = self.right.reverse() if self.right is not None else None
|
|
62
|
+
new_right = self.left.reverse() if self.left is not None else None
|
|
63
|
+
return BinaryNode(self.data, new_left, new_right)
|
|
64
|
+
|
|
65
|
+
def depth(self) -> int:
|
|
66
|
+
return MultiNode.depth(self)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
"BinaryNode"
|
|
71
|
+
]
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, Generator, List as List, Set as Set, Dict as Dict, Generic, \
|
|
3
|
+
TypeVar, Iterable, Iterator
|
|
4
|
+
from ...logging_.utils import get_logger
|
|
5
|
+
from ..queue import Queue
|
|
6
|
+
from .multinode import MultiNode
|
|
7
|
+
from ...reflection import get_python_version
|
|
8
|
+
|
|
9
|
+
if get_python_version() >= (3, 9):
|
|
10
|
+
from builtins import list as List, set as Set, dict as Dict
|
|
11
|
+
|
|
12
|
+
T = TypeVar("T")
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Graph(Generic[T]):
|
|
18
|
+
"""A general-purpose Graph class.
|
|
19
|
+
|
|
20
|
+
This class represents a directed graph, where nodes can be connected through edges.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
nodes (Optional[List[MultiNode]]): A list of MultiNode instances representing the nodes in the graph.
|
|
24
|
+
Default is an empty list.
|
|
25
|
+
|
|
26
|
+
Methods:
|
|
27
|
+
__init__(self, nodes: Optional[List[MultiNode]] = None): Initialize the Graph with given nodes.
|
|
28
|
+
add_node(self, node): Add a node to the graph.
|
|
29
|
+
_extended_dfs(self) -> Generator: Perform an extended depth-first search on the graph.
|
|
30
|
+
dfs(self) -> Generator: Perform a depth-first search on the graph.
|
|
31
|
+
topological_sort(self) -> list: Get a topological sort of the graph nodes.
|
|
32
|
+
bfs(self) -> Generator: Perform a breadth-first search on the graph.
|
|
33
|
+
__str__(self) -> str: Get a string representation of the graph.
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[T, Set[T]]:
|
|
38
|
+
"""
|
|
39
|
+
converts the graph to a dictionary.
|
|
40
|
+
Returns:
|
|
41
|
+
dict: A dictionary representing the graph.
|
|
42
|
+
"""
|
|
43
|
+
logger.debug("Converting graph with %s nodes to dictionary", len(self.nodes))
|
|
44
|
+
dct: Dict[T, Set[T]] = {}
|
|
45
|
+
for node in self:
|
|
46
|
+
v = dct.get(node.data, set())
|
|
47
|
+
for child in node:
|
|
48
|
+
v.add(child.data)
|
|
49
|
+
dct[node.data] = v
|
|
50
|
+
logger.debug("Graph converted to dictionary with %s entries", len(dct))
|
|
51
|
+
return dct
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def from_dict(dct: Dict[T, Iterable[T]]) -> "Graph[T]":
|
|
55
|
+
"""
|
|
56
|
+
converts a dictionary to a graph.
|
|
57
|
+
Args:
|
|
58
|
+
dct: A dictionary representing the graph.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Graph[T]: A graph representing the given dictionary.
|
|
62
|
+
"""
|
|
63
|
+
logger.debug("Creating graph from dictionary with %s entries", len(dct))
|
|
64
|
+
g: Graph[T] = Graph()
|
|
65
|
+
seen: Dict[T, MultiNode[T]] = {}
|
|
66
|
+
for k, v in dct.items():
|
|
67
|
+
seen[k] = seen.get(k, MultiNode(k))
|
|
68
|
+
|
|
69
|
+
for o in v:
|
|
70
|
+
seen[o] = seen.get(o, MultiNode(o))
|
|
71
|
+
seen[k].add_child(seen[o])
|
|
72
|
+
|
|
73
|
+
g.add_node(seen[k])
|
|
74
|
+
logger.info("Graph created from dictionary with %s nodes", len(g.nodes))
|
|
75
|
+
return g
|
|
76
|
+
|
|
77
|
+
def __init__(self, nodes: Optional[List[MultiNode[T]]] = None):
|
|
78
|
+
self.nodes: List[MultiNode[T]] = nodes if nodes is not None else []
|
|
79
|
+
logger.debug("Graph initialized with %s nodes", len(self.nodes))
|
|
80
|
+
|
|
81
|
+
def add_node(self, node: MultiNode[T]) -> None:
|
|
82
|
+
"""Add a node to the graph.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
node: The MultiNode instance to add to the graph.
|
|
86
|
+
"""
|
|
87
|
+
self.nodes.append(node)
|
|
88
|
+
logger.debug("Added node to graph, total nodes: %s", len(self.nodes))
|
|
89
|
+
|
|
90
|
+
def _extended_dfs(self) -> Generator[MultiNode[T], None, List[MultiNode[T]]]:
|
|
91
|
+
"""Perform an extended depth-first search on the graph.
|
|
92
|
+
|
|
93
|
+
This private method performs an extended depth-first search (DFS) on the graph,
|
|
94
|
+
keeping track of enter and exit times for each node, and returns a generator that yields
|
|
95
|
+
nodes in the order of DFS traversal.
|
|
96
|
+
|
|
97
|
+
Yields:
|
|
98
|
+
Generator: The MultiNode instances in the order of depth-first traversal.
|
|
99
|
+
"""
|
|
100
|
+
seen: set = set()
|
|
101
|
+
enter_times: dict = {}
|
|
102
|
+
exit_times: dict = {}
|
|
103
|
+
travel_index: int = 1
|
|
104
|
+
all_nodes: List[MultiNode] = []
|
|
105
|
+
|
|
106
|
+
def handle_node(node: MultiNode[T]) -> Generator[MultiNode[T], None, None]:
|
|
107
|
+
nonlocal travel_index
|
|
108
|
+
seen.add(node)
|
|
109
|
+
all_nodes.append(node)
|
|
110
|
+
yield node
|
|
111
|
+
for subnode in node._children: # pylint: disable=protected-access
|
|
112
|
+
if subnode not in seen:
|
|
113
|
+
travel_index += 1
|
|
114
|
+
enter_times[subnode] = travel_index
|
|
115
|
+
if subnode is not None:
|
|
116
|
+
yield from handle_node(subnode)
|
|
117
|
+
travel_index += 1
|
|
118
|
+
exit_times[subnode] = travel_index
|
|
119
|
+
|
|
120
|
+
for node in self.nodes:
|
|
121
|
+
if node not in seen:
|
|
122
|
+
enter_times[node] = travel_index
|
|
123
|
+
travel_index += 1
|
|
124
|
+
yield from handle_node(node)
|
|
125
|
+
travel_index += 1
|
|
126
|
+
exit_times[node] = travel_index
|
|
127
|
+
topological_order = sorted(
|
|
128
|
+
all_nodes, key=lambda v: exit_times[v], reverse=True)
|
|
129
|
+
return topological_order
|
|
130
|
+
|
|
131
|
+
# def topological_sort(graph: Graph):
|
|
132
|
+
# def dfs(node: MultiNode, visited: set, result: list):
|
|
133
|
+
# visited.add(node)
|
|
134
|
+
# for neighbor in node:
|
|
135
|
+
# if neighbor not in visited:
|
|
136
|
+
# dfs(neighbor, visited, result)
|
|
137
|
+
# result.append(node)
|
|
138
|
+
#
|
|
139
|
+
# visited: set = set()
|
|
140
|
+
# result: list = []
|
|
141
|
+
# for node in graph:
|
|
142
|
+
# if node not in visited:
|
|
143
|
+
# dfs(node, visited, result)
|
|
144
|
+
# return result[::-1] # Reverse the result list
|
|
145
|
+
|
|
146
|
+
def dfs(self) -> Generator[MultiNode[T], None, None]:
|
|
147
|
+
"""Perform a depth-first search on the graph.
|
|
148
|
+
|
|
149
|
+
This method performs a depth-first search (DFS) on the graph using the private _extended_dfs method.
|
|
150
|
+
|
|
151
|
+
Yields:
|
|
152
|
+
Generator: The MultiNode instances in the order of depth-first traversal.
|
|
153
|
+
"""
|
|
154
|
+
logger.debug("Starting DFS traversal on graph with %s nodes", len(self.nodes))
|
|
155
|
+
yield from self._extended_dfs()
|
|
156
|
+
|
|
157
|
+
def topological_sort(self) -> List[MultiNode[T]]:
|
|
158
|
+
"""Get a topological sort of the graph nodes.
|
|
159
|
+
|
|
160
|
+
This method performs a topological sort on the graph using the private _extended_dfs method.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
list: A list containing the MultiNode instances in topological order.
|
|
164
|
+
"""
|
|
165
|
+
logger.debug("Starting topological sort on graph with %s nodes", len(self.nodes))
|
|
166
|
+
g = self._extended_dfs()
|
|
167
|
+
try:
|
|
168
|
+
while True:
|
|
169
|
+
next(g)
|
|
170
|
+
except StopIteration as e:
|
|
171
|
+
result = e.value
|
|
172
|
+
logger.info("Topological sort completed, result has %s nodes", len(result))
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
def bfs(self) -> Generator[MultiNode[T], None, None]:
|
|
176
|
+
"""Perform a breadth-first search on the graph.
|
|
177
|
+
|
|
178
|
+
This method performs a breadth-first search (BFS) on the graph using a queue.
|
|
179
|
+
|
|
180
|
+
Yields:
|
|
181
|
+
Generator: The MultiNode instances in the order of breadth-first traversal.
|
|
182
|
+
"""
|
|
183
|
+
logger.debug("Starting BFS traversal on graph with %s nodes", len(self.nodes))
|
|
184
|
+
q: Queue[MultiNode[T]] = Queue()
|
|
185
|
+
for node in self.nodes:
|
|
186
|
+
q.push(node)
|
|
187
|
+
seen: Set[MultiNode] = set()
|
|
188
|
+
for node in q:
|
|
189
|
+
if node not in seen:
|
|
190
|
+
seen.add(node)
|
|
191
|
+
yield node
|
|
192
|
+
for child in node._children: # pylint: disable=protected-access
|
|
193
|
+
q.push(child) # type:ignore
|
|
194
|
+
logger.debug("BFS traversal completed, visited %s nodes", len(seen))
|
|
195
|
+
|
|
196
|
+
def __str__(self) -> str:
|
|
197
|
+
tmp = []
|
|
198
|
+
for n in self.dfs():
|
|
199
|
+
tmp.append(f"\t{str(n)}")
|
|
200
|
+
return "Graph(\n" + ",\n".join(tmp) + "\n)"
|
|
201
|
+
|
|
202
|
+
def __iter__(self) -> Iterator[MultiNode[T]]:
|
|
203
|
+
return iter(self.nodes)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
__all__ = [
|
|
207
|
+
"Graph"
|
|
208
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from typing import Optional, Generator, TypeVar, Generic, List as List, Set
|
|
2
|
+
|
|
3
|
+
from ...reflection import get_python_version
|
|
4
|
+
|
|
5
|
+
if get_python_version() >= (3, 9):
|
|
6
|
+
from builtins import list as List
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MultiNode(Generic[T]):
|
|
11
|
+
"""A node class with no limit to children amount
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, data: T, children: Optional[List[Optional['MultiNode[T]']]] = None):
|
|
15
|
+
self.data: T = data
|
|
16
|
+
self._children: List[Optional[MultiNode[T]]] = children if children is not None else []
|
|
17
|
+
|
|
18
|
+
def __getitem__(self, index) -> T:
|
|
19
|
+
return self._children[index]
|
|
20
|
+
|
|
21
|
+
def __setitem__(self, value: T, index: int) -> None:
|
|
22
|
+
self._children[index] = value # type:ignore
|
|
23
|
+
|
|
24
|
+
def __len__(self) -> int:
|
|
25
|
+
return len(self._children)
|
|
26
|
+
|
|
27
|
+
def __iter__(self) -> Generator["MultiNode[T]", None, None]:
|
|
28
|
+
yield from self._children
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
res = ""
|
|
32
|
+
seen = set()
|
|
33
|
+
|
|
34
|
+
def handle_node(node: MultiNode):
|
|
35
|
+
nonlocal res
|
|
36
|
+
# res += f"MultiNode({node.data}, ["
|
|
37
|
+
seen.add(node)
|
|
38
|
+
tmp = []
|
|
39
|
+
for child in node._children: # pylint: disable=protected-access
|
|
40
|
+
if child in seen:
|
|
41
|
+
tmp.append("...")
|
|
42
|
+
else:
|
|
43
|
+
if child is not None:
|
|
44
|
+
tmp.append(handle_node(child))
|
|
45
|
+
child_str = ", [" + ", ".join(tmp) + "]" if len(tmp) > 0 else ""
|
|
46
|
+
return f"{node.__class__.__name__}({node.data}{child_str})"
|
|
47
|
+
|
|
48
|
+
return handle_node(self)
|
|
49
|
+
|
|
50
|
+
def __repr__(self):
|
|
51
|
+
return str(self)
|
|
52
|
+
|
|
53
|
+
def __eq__(self, other) -> bool:
|
|
54
|
+
if not isinstance(other, MultiNode):
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
return self.data == other.data and len(self) == len(other) and all(
|
|
58
|
+
[a.data == b.data for a, b in zip(self, other)])
|
|
59
|
+
|
|
60
|
+
def __hash__(self) -> int:
|
|
61
|
+
return hash((self.__class__, self.data, len(self), (c.data for c in self)))
|
|
62
|
+
|
|
63
|
+
def __reversed__(self) -> 'MultiNode[T]':
|
|
64
|
+
return self.reverse()
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def is_leaf(self) -> bool:
|
|
68
|
+
return len(self._children) == 0
|
|
69
|
+
|
|
70
|
+
def reverse(self) -> 'MultiNode[T]':
|
|
71
|
+
return MultiNode(self.data, self._children[::-1])
|
|
72
|
+
|
|
73
|
+
def add_child(self, child: "MultiNode[T]") -> None:
|
|
74
|
+
"""adds a child to current node
|
|
75
|
+
"""
|
|
76
|
+
self._children.append(child)
|
|
77
|
+
|
|
78
|
+
def depth(self) -> int:
|
|
79
|
+
"""
|
|
80
|
+
Returns the depth of the current node (including).
|
|
81
|
+
Returns:
|
|
82
|
+
int
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def helper(cur: MultiNode, seen: Optional[Set[MultiNode]] = None) -> int:
|
|
86
|
+
if not isinstance(cur, MultiNode):
|
|
87
|
+
return 0
|
|
88
|
+
if seen is None:
|
|
89
|
+
seen = set()
|
|
90
|
+
elif cur in seen:
|
|
91
|
+
return 0
|
|
92
|
+
seen.add(cur)
|
|
93
|
+
seq = [helper(child, seen) for child in cur if ((child is not None) and (child not in seen))]
|
|
94
|
+
return 1 + (max(seq) if len(seq) > 0 else 0)
|
|
95
|
+
|
|
96
|
+
return helper(self)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
"MultiNode"
|
|
101
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional, TypeVar
|
|
3
|
+
from ...metaclasses import ImplicitDataDeleterMeta
|
|
4
|
+
from .multinode import MultiNode
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Node(MultiNode[T], metaclass=ImplicitDataDeleterMeta):
|
|
10
|
+
"""A 'classic' node class with only one child
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, data: T, next: Optional[Node[T]] = None): # pylint: disable=redefined-builtin
|
|
14
|
+
super().__init__(data, [next])
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def next(self) -> "Node[T]":
|
|
18
|
+
"""return the next node after self
|
|
19
|
+
"""
|
|
20
|
+
return self._children[0] # type:ignore
|
|
21
|
+
|
|
22
|
+
@next.setter
|
|
23
|
+
def next(self, value: Node[T]) -> None:
|
|
24
|
+
self._children[0] = value
|
|
25
|
+
|
|
26
|
+
def __str__(self):
|
|
27
|
+
# res = ""
|
|
28
|
+
# seen = set()
|
|
29
|
+
|
|
30
|
+
# def handle_node(node: Node):
|
|
31
|
+
# nonlocal res
|
|
32
|
+
# if node in seen:
|
|
33
|
+
# res += "..."
|
|
34
|
+
# else:
|
|
35
|
+
# seen.add(node)
|
|
36
|
+
# res += f"Node({node.data}, "
|
|
37
|
+
# if node.next is None:
|
|
38
|
+
# res += "None)"
|
|
39
|
+
# elif node.next in seen:
|
|
40
|
+
# res += "...)"
|
|
41
|
+
|
|
42
|
+
# curr = self
|
|
43
|
+
# while curr is not None:
|
|
44
|
+
# handle_node(curr)
|
|
45
|
+
# curr = curr.next
|
|
46
|
+
# if curr in seen:
|
|
47
|
+
# break
|
|
48
|
+
# return res+")"
|
|
49
|
+
return MultiNode.__str__(self).replace(
|
|
50
|
+
self.__class__.__mro__[1].__name__,
|
|
51
|
+
self.__class__.__name__
|
|
52
|
+
).replace("[", "").replace("]", "")
|
|
53
|
+
|
|
54
|
+
def __repr__(self):
|
|
55
|
+
return str(self)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"Node"
|
|
60
|
+
]
|