typemore 0.1.0__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.
- typemore-0.1.0/LICENSE +21 -0
- typemore-0.1.0/PKG-INFO +44 -0
- typemore-0.1.0/README.md +17 -0
- typemore-0.1.0/pyproject.toml +71 -0
- typemore-0.1.0/typemore/_PackageTools.py +12 -0
- typemore-0.1.0/typemore/__init__.py +36 -0
- typemore-0.1.0/typemore/py.typed +0 -0
- typemore-0.1.0/typemore/types/__init__.py +0 -0
- typemore-0.1.0/typemore/types/auto.py +119 -0
- typemore-0.1.0/typemore/types/chainNode.py +293 -0
- typemore-0.1.0/typemore/types/debug.py +239 -0
- typemore-0.1.0/typemore/types/displayProxy.py +264 -0
- typemore-0.1.0/typemore/types/infiniteIterator.py +52 -0
- typemore-0.1.0/typemore/types/interval.py +358 -0
- typemore-0.1.0/typemore/types/lenLimit.py +105 -0
- typemore-0.1.0/typemore/types/memLimit.py +167 -0
- typemore-0.1.0/typemore/types/point2D.py +131 -0
- typemore-0.1.0/typemore/types/point3D.py +133 -0
- typemore-0.1.0/typemore/types/pointAnyD.py +201 -0
- typemore-0.1.0/typemore/types/range.py +271 -0
- typemore-0.1.0/typemore/types/readOnlyBox.py +66 -0
- typemore-0.1.0/typemore/types/strictFloat.py +164 -0
- typemore-0.1.0/typemore/types/timeLimit.py +66 -0
typemore-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 maoshen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
typemore-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: typemore
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A collection of custom Python data types for specialized behavior.
|
|
5
|
+
Author: maoshen
|
|
6
|
+
Maintainer: maoshen
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: Auto,ChainNode,Debug,DisplayProxy,InfiniteIterator,Interval,LenLimit,LenLimitError,MemLimit,MemLimitError,Point2D,Point3D,PointAnyD,Range,ReadOnlyBox,StrictFloat,TimeLimit,TimeLimitError,more,type,typemore
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pympler>=1.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: build; extra == 'dev'
|
|
25
|
+
Requires-Dist: twine; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
## License
|
|
29
|
+
|
|
30
|
+
MIT
|
|
31
|
+
|
|
32
|
+
# typemore
|
|
33
|
+
|
|
34
|
+
A collection of custom Python data types for specialized behavior.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
pip install typemore
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
from typemore import StrictFloat
|
|
43
|
+
|
|
44
|
+
StrictFloat(0.1) + StrictFloat(0.2) # StrictFloat:0.3
|
typemore-0.1.0/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## License
|
|
2
|
+
|
|
3
|
+
MIT
|
|
4
|
+
|
|
5
|
+
# typemore
|
|
6
|
+
|
|
7
|
+
A collection of custom Python data types for specialized behavior.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
pip install typemore
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
from typemore import StrictFloat
|
|
16
|
+
|
|
17
|
+
StrictFloat(0.1) + StrictFloat(0.2) # StrictFloat:0.3
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "typemore"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A collection of custom Python data types for specialized behavior."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
keywords = [
|
|
14
|
+
"typemore",
|
|
15
|
+
"type",
|
|
16
|
+
"more",
|
|
17
|
+
"Range",
|
|
18
|
+
"Point2D",
|
|
19
|
+
"Point3D",
|
|
20
|
+
"PointAnyD",
|
|
21
|
+
"Auto",
|
|
22
|
+
"Debug",
|
|
23
|
+
"StrictFloat",
|
|
24
|
+
"Interval",
|
|
25
|
+
"ChainNode",
|
|
26
|
+
"InfiniteIterator",
|
|
27
|
+
"ReadOnlyBox",
|
|
28
|
+
"DisplayProxy",
|
|
29
|
+
"LenLimit",
|
|
30
|
+
"LenLimitError",
|
|
31
|
+
"TimeLimit",
|
|
32
|
+
"TimeLimitError",
|
|
33
|
+
"MemLimit",
|
|
34
|
+
"MemLimitError"]
|
|
35
|
+
classifiers = [
|
|
36
|
+
"Development Status :: 3 - Alpha",
|
|
37
|
+
"Intended Audience :: Developers",
|
|
38
|
+
"License :: OSI Approved :: MIT License",
|
|
39
|
+
"Operating System :: OS Independent",
|
|
40
|
+
"Programming Language :: Python :: 3",
|
|
41
|
+
"Programming Language :: Python :: 3.10",
|
|
42
|
+
"Programming Language :: Python :: 3.11",
|
|
43
|
+
"Programming Language :: Python :: 3.12",
|
|
44
|
+
"Programming Language :: Python :: 3.13",
|
|
45
|
+
"Topic :: Software Development :: Libraries",
|
|
46
|
+
"Typing :: Typed",
|
|
47
|
+
]
|
|
48
|
+
authors = [
|
|
49
|
+
{ name = "maoshen" }
|
|
50
|
+
]
|
|
51
|
+
maintainers = [
|
|
52
|
+
{ name = "maoshen" }
|
|
53
|
+
]
|
|
54
|
+
dependencies = [
|
|
55
|
+
"pympler>=1.0"
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.optional-dependencies]
|
|
59
|
+
dev = [
|
|
60
|
+
"build",
|
|
61
|
+
"twine",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.hatch.build.targets.wheel]
|
|
65
|
+
packages = ["typemore"]
|
|
66
|
+
|
|
67
|
+
[tool.hatch.build.targets.sdist]
|
|
68
|
+
include = [
|
|
69
|
+
"/typemore",
|
|
70
|
+
"/README.md",
|
|
71
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
def private(func):
|
|
4
|
+
def wrapper(*args, **kwargs):
|
|
5
|
+
caller_qualname = sys._getframe(1).f_code.co_qualname
|
|
6
|
+
method_qualname = func.__qualname__
|
|
7
|
+
parent = method_qualname[:method_qualname.rfind('.')]
|
|
8
|
+
|
|
9
|
+
if not caller_qualname.startswith(parent):
|
|
10
|
+
raise RuntimeError(f"{method_qualname} is private")
|
|
11
|
+
return func(*args, **kwargs)
|
|
12
|
+
return wrapper
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from .types.range import Range
|
|
2
|
+
from .types.point2D import Point2D
|
|
3
|
+
from .types.point3D import Point3D
|
|
4
|
+
from .types.pointAnyD import PointAnyD
|
|
5
|
+
from .types.auto import Auto
|
|
6
|
+
from .types.debug import Debug
|
|
7
|
+
from .types.strictFloat import StrictFloat
|
|
8
|
+
from .types.interval import Interval
|
|
9
|
+
from .types.chainNode import ChainNode
|
|
10
|
+
from .types.infiniteIterator import InfiniteIterator
|
|
11
|
+
from .types.readOnlyBox import ReadOnlyBox
|
|
12
|
+
from .types.displayProxy import DisplayProxy
|
|
13
|
+
from .types.lenLimit import LenLimit, LenLimitError
|
|
14
|
+
from .types.timeLimit import TimeLimit, TimeLimitError
|
|
15
|
+
from .types.memLimit import MemLimit, MemLimitError
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__author__ = "maoshen"
|
|
19
|
+
__all__ = ["Range",
|
|
20
|
+
"Point2D",
|
|
21
|
+
"Point3D",
|
|
22
|
+
"PointAnyD",
|
|
23
|
+
"Auto",
|
|
24
|
+
"Debug",
|
|
25
|
+
"StrictFloat",
|
|
26
|
+
"Interval",
|
|
27
|
+
"ChainNode",
|
|
28
|
+
"InfiniteIterator",
|
|
29
|
+
"ReadOnlyBox",
|
|
30
|
+
"DisplayProxy",
|
|
31
|
+
"LenLimit",
|
|
32
|
+
"LenLimitError",
|
|
33
|
+
"TimeLimit",
|
|
34
|
+
"TimeLimitError",
|
|
35
|
+
"MemLimit",
|
|
36
|
+
"MemLimitError"]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
class Auto:
|
|
2
|
+
"""
|
|
3
|
+
This class is compatible with most commonly used type-conversion functions.
|
|
4
|
+
It prevents exceptions for conversion operations that would normally raise them.
|
|
5
|
+
"""
|
|
6
|
+
_cache = {}
|
|
7
|
+
_hash = None
|
|
8
|
+
|
|
9
|
+
def __new__(cls, auto):
|
|
10
|
+
base = type(auto)
|
|
11
|
+
if base not in cls._cache:
|
|
12
|
+
if base in (bool, type(None)):
|
|
13
|
+
bases = (cls,)
|
|
14
|
+
else:
|
|
15
|
+
bases = (cls, base)
|
|
16
|
+
cls._cache[base] = type(
|
|
17
|
+
f"{cls.__name__}[{base.__name__}]",
|
|
18
|
+
bases,
|
|
19
|
+
{}
|
|
20
|
+
)
|
|
21
|
+
new_cls = cls._cache[base]
|
|
22
|
+
|
|
23
|
+
if auto is None or base is bool:
|
|
24
|
+
instance = super().__new__(new_cls)
|
|
25
|
+
elif issubclass(base, (int, float, complex, str, tuple, frozenset, bytes)):
|
|
26
|
+
instance = base.__new__(new_cls, auto)
|
|
27
|
+
elif issubclass(base, (list, dict, set, bytearray)):
|
|
28
|
+
instance = base.__new__(new_cls)
|
|
29
|
+
base.__init__(instance, auto)
|
|
30
|
+
else:
|
|
31
|
+
instance = base.__new__(new_cls)
|
|
32
|
+
return instance
|
|
33
|
+
|
|
34
|
+
def __init__(self, auto):
|
|
35
|
+
self.auto = self
|
|
36
|
+
self._hash = None
|
|
37
|
+
try:
|
|
38
|
+
hash(auto)
|
|
39
|
+
self._hash = True
|
|
40
|
+
except TypeError:
|
|
41
|
+
self._hash = False
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return f"{self.__class__.__name__}:{super().__repr__()}"
|
|
45
|
+
|
|
46
|
+
def __getattr__(self, name):
|
|
47
|
+
raise AttributeError(name)
|
|
48
|
+
|
|
49
|
+
def __dir__(self):
|
|
50
|
+
return list(set(dir(self.__class__) + dir(self.__class__.__mro__[1]) + ['auto']))
|
|
51
|
+
|
|
52
|
+
def __int__(self):
|
|
53
|
+
if not self._hash:
|
|
54
|
+
return len(self)
|
|
55
|
+
if isinstance(self, bool):
|
|
56
|
+
return 1 if bool.__bool__(self) else 0
|
|
57
|
+
if isinstance(self, int):
|
|
58
|
+
return int.__int__(self)
|
|
59
|
+
if isinstance(self, float):
|
|
60
|
+
return int(float.__int__(self))
|
|
61
|
+
if isinstance(self, complex):
|
|
62
|
+
return int(self.real)
|
|
63
|
+
if isinstance(self, str):
|
|
64
|
+
try:
|
|
65
|
+
return int(str.__str__(self))
|
|
66
|
+
except ValueError:
|
|
67
|
+
return 1 if str.__len__(self) else 0
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
def __float__(self) -> float:
|
|
71
|
+
return float(self.__int__())
|
|
72
|
+
|
|
73
|
+
def __complex__(self) -> complex:
|
|
74
|
+
if self._hash:
|
|
75
|
+
if isinstance(self.auto, complex):
|
|
76
|
+
return complex(self.real, self.imag)
|
|
77
|
+
elif isinstance(self.auto, int | float):
|
|
78
|
+
return complex(self.auto, 0j)
|
|
79
|
+
elif isinstance(self.auto, bool):
|
|
80
|
+
if self.auto:
|
|
81
|
+
real = 1
|
|
82
|
+
else:
|
|
83
|
+
real = 0
|
|
84
|
+
return complex(real, 0j)
|
|
85
|
+
elif isinstance(self.auto, str):
|
|
86
|
+
try:
|
|
87
|
+
return complex(self.auto)
|
|
88
|
+
except ValueError:
|
|
89
|
+
if self.auto.strip() == '':
|
|
90
|
+
return 0
|
|
91
|
+
else:
|
|
92
|
+
return 1
|
|
93
|
+
return complex(0)
|
|
94
|
+
else:
|
|
95
|
+
return complex(self.__int__(), 0j)
|
|
96
|
+
|
|
97
|
+
def __index__(self) -> int:
|
|
98
|
+
if self._hash:
|
|
99
|
+
if isinstance(self.auto, int | float | bool):
|
|
100
|
+
return int(self.auto)
|
|
101
|
+
elif isinstance(self.auto, str):
|
|
102
|
+
try:
|
|
103
|
+
return int(self.auto)
|
|
104
|
+
except ValueError:
|
|
105
|
+
if self.auto.strip() == '':
|
|
106
|
+
return 0
|
|
107
|
+
else:
|
|
108
|
+
return 1
|
|
109
|
+
elif isinstance(self.auto, complex):
|
|
110
|
+
return int(self.auto.real)
|
|
111
|
+
return 0
|
|
112
|
+
else:
|
|
113
|
+
return self.__int__()
|
|
114
|
+
|
|
115
|
+
def __iter__(self):
|
|
116
|
+
auto = self.auto
|
|
117
|
+
if isinstance(auto, (int, float, complex, bool, bytes)):
|
|
118
|
+
return iter([auto])
|
|
119
|
+
return iter(auto)
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
class PointerNone:
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
point_none = PointerNone()
|
|
7
|
+
|
|
8
|
+
class ChainNode:
|
|
9
|
+
"""
|
|
10
|
+
A singly linked list node whose class also inherits from type(value).
|
|
11
|
+
|
|
12
|
+
Each instance stores a value and a pointer to the next node. The class
|
|
13
|
+
of an instance is generated dynamically based on the type of value, so
|
|
14
|
+
a node can also use the methods and operators of its underlying type.
|
|
15
|
+
For example, ChainNode(5) inherits from int, and ChainNode("a") inherits
|
|
16
|
+
from str.
|
|
17
|
+
|
|
18
|
+
The end of a chain is marked by point_none, a sentinel instance of
|
|
19
|
+
PointerNone. Use "pointer is point_none" to test for the end.
|
|
20
|
+
"""
|
|
21
|
+
_cache = {}
|
|
22
|
+
|
|
23
|
+
def __new__(cls, value, pointer = point_none):
|
|
24
|
+
base = type(value)
|
|
25
|
+
if base not in cls._cache:
|
|
26
|
+
if base in (bool, type(None)):
|
|
27
|
+
bases = (cls,)
|
|
28
|
+
else:
|
|
29
|
+
bases = (cls, base)
|
|
30
|
+
cls._cache[base] = type(
|
|
31
|
+
f"{cls.__name__}[{base.__name__}]",
|
|
32
|
+
bases,
|
|
33
|
+
{}
|
|
34
|
+
)
|
|
35
|
+
new_cls = cls._cache[base]
|
|
36
|
+
if value is None or base is bool:
|
|
37
|
+
instance = super().__new__(new_cls)
|
|
38
|
+
return instance
|
|
39
|
+
elif issubclass(base, (int, float, str, tuple, frozenset, bytes)):
|
|
40
|
+
instance= base.__new__(new_cls, value)
|
|
41
|
+
return instance
|
|
42
|
+
else:
|
|
43
|
+
instance = base.__new__(new_cls)
|
|
44
|
+
return instance
|
|
45
|
+
|
|
46
|
+
def __init__(self, value, pointer = point_none):
|
|
47
|
+
self.value = value
|
|
48
|
+
self.pointer = pointer
|
|
49
|
+
|
|
50
|
+
def __repr__(self) -> str:
|
|
51
|
+
return f"Pointer({self.value}, {self.pointer if self.pointer is not point_none else ''})"
|
|
52
|
+
|
|
53
|
+
def __str__(self) -> str:
|
|
54
|
+
return f"({self.value}, {self.pointer if self.pointer is not point_none else ''})"
|
|
55
|
+
|
|
56
|
+
def getvalue_number(self, number: int):
|
|
57
|
+
"""
|
|
58
|
+
Return the value of the node at index number, where 0 is self.
|
|
59
|
+
|
|
60
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
61
|
+
if the chain ends before reaching that index.
|
|
62
|
+
"""
|
|
63
|
+
if not isinstance(number, int) or number < 0:
|
|
64
|
+
raise ValueError("index out of range")
|
|
65
|
+
node = self
|
|
66
|
+
for _ in range(number):
|
|
67
|
+
if not isinstance(node.pointer, ChainNode):
|
|
68
|
+
raise IndexError("index out of range")
|
|
69
|
+
node = node.pointer
|
|
70
|
+
return node.value
|
|
71
|
+
|
|
72
|
+
def getpointer_number(self, number: int):
|
|
73
|
+
"""
|
|
74
|
+
Return the node at index number, where 0 is self.
|
|
75
|
+
|
|
76
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
77
|
+
if the chain ends before reaching that index.
|
|
78
|
+
"""
|
|
79
|
+
if not isinstance(number, int) or number < 0:
|
|
80
|
+
raise ValueError("index out of range")
|
|
81
|
+
node = self
|
|
82
|
+
for _ in range(number):
|
|
83
|
+
if not isinstance(node.pointer, ChainNode):
|
|
84
|
+
raise IndexError("index out of range")
|
|
85
|
+
node = node.pointer
|
|
86
|
+
return node
|
|
87
|
+
|
|
88
|
+
def getpointer_value(self, value):
|
|
89
|
+
"""
|
|
90
|
+
Return the pointer of the first node whose value equals value.
|
|
91
|
+
|
|
92
|
+
Raises ValueError if no such node exists.
|
|
93
|
+
"""
|
|
94
|
+
node = self._find_node(value)
|
|
95
|
+
if node is None:
|
|
96
|
+
raise ValueError("value not found in chain")
|
|
97
|
+
return node.pointer
|
|
98
|
+
|
|
99
|
+
def setpointer_number(self, number: int, pointer) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Set the pointer of the node at index number, where 0 is self.
|
|
102
|
+
|
|
103
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
104
|
+
if the chain ends before reaching that index.
|
|
105
|
+
"""
|
|
106
|
+
if not isinstance(number, int) or number < 0:
|
|
107
|
+
raise ValueError("index out of range")
|
|
108
|
+
node = self
|
|
109
|
+
for _ in range(number):
|
|
110
|
+
if not isinstance(node, ChainNode):
|
|
111
|
+
raise IndexError("index out of range")
|
|
112
|
+
node = node.pointer
|
|
113
|
+
if not isinstance(node, ChainNode):
|
|
114
|
+
raise IndexError("index out of range")
|
|
115
|
+
node.pointer = pointer
|
|
116
|
+
|
|
117
|
+
def setpointer_value(self, value, pointer) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Set the pointer of the first node whose value equals value.
|
|
120
|
+
|
|
121
|
+
Raises ValueError if no such node exists.
|
|
122
|
+
"""
|
|
123
|
+
node = self._find_node(value)
|
|
124
|
+
if node is None:
|
|
125
|
+
raise ValueError("value not found in chain")
|
|
126
|
+
node.pointer = pointer
|
|
127
|
+
|
|
128
|
+
def setvalue_number(self, number: int, value) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Set the value attribute of the node at index number, where 0 is self.
|
|
131
|
+
|
|
132
|
+
For immutable subclasses such as ChainNode[int], the instance's own
|
|
133
|
+
numeric value does not change; only the value attribute is updated.
|
|
134
|
+
Raises ValueError if number is not a non-negative int, and IndexError
|
|
135
|
+
if the chain ends before reaching that index.
|
|
136
|
+
"""
|
|
137
|
+
if not isinstance(number, int) or number < 0:
|
|
138
|
+
raise ValueError("index out of range")
|
|
139
|
+
node = self
|
|
140
|
+
for _ in range(number):
|
|
141
|
+
if not isinstance(node, ChainNode):
|
|
142
|
+
raise IndexError("index out of range")
|
|
143
|
+
node = node.pointer
|
|
144
|
+
if not isinstance(node, ChainNode):
|
|
145
|
+
raise IndexError("index out of range")
|
|
146
|
+
node.value = value
|
|
147
|
+
|
|
148
|
+
def delete_number(self, number: int):
|
|
149
|
+
"""
|
|
150
|
+
Delete the node at index number.
|
|
151
|
+
|
|
152
|
+
If number is 0, return the next node as the new head. Otherwise, return
|
|
153
|
+
self. Raises ValueError if number is not a non-negative int, and
|
|
154
|
+
IndexError if the chain ends before reaching that index.
|
|
155
|
+
"""
|
|
156
|
+
if not isinstance(number, int) or number < 0:
|
|
157
|
+
raise ValueError("index out of range")
|
|
158
|
+
|
|
159
|
+
if number == 0:
|
|
160
|
+
if not isinstance(self.pointer, ChainNode):
|
|
161
|
+
raise IndexError("index out of range")
|
|
162
|
+
return self.pointer
|
|
163
|
+
|
|
164
|
+
prev = self
|
|
165
|
+
for _ in range(number - 1):
|
|
166
|
+
if not isinstance(prev.pointer, ChainNode):
|
|
167
|
+
raise IndexError("index out of range")
|
|
168
|
+
prev = prev.pointer
|
|
169
|
+
|
|
170
|
+
target = prev.pointer
|
|
171
|
+
if not isinstance(target, ChainNode):
|
|
172
|
+
raise IndexError("index out of range")
|
|
173
|
+
prev.pointer = target.pointer
|
|
174
|
+
return self
|
|
175
|
+
|
|
176
|
+
def delete_value(self, value):
|
|
177
|
+
"""
|
|
178
|
+
Delete the first node whose value equals value.
|
|
179
|
+
|
|
180
|
+
Return the new head. When the deleted node is the head, the successor
|
|
181
|
+
becomes the new head. Raises ValueError if no such node exists.
|
|
182
|
+
"""
|
|
183
|
+
if self.value == value:
|
|
184
|
+
if not isinstance(self.pointer, ChainNode):
|
|
185
|
+
raise ValueError("value not found in chain")
|
|
186
|
+
return self.pointer
|
|
187
|
+
|
|
188
|
+
prev = self
|
|
189
|
+
cur = self.pointer
|
|
190
|
+
while isinstance(cur, ChainNode):
|
|
191
|
+
if cur.value == value:
|
|
192
|
+
prev.pointer = cur.pointer
|
|
193
|
+
return self
|
|
194
|
+
prev = cur
|
|
195
|
+
cur = cur.pointer
|
|
196
|
+
raise ValueError("value not found in chain")
|
|
197
|
+
|
|
198
|
+
def node_search(self, value) -> int:
|
|
199
|
+
"""
|
|
200
|
+
Return the index of the first node whose value equals value.
|
|
201
|
+
|
|
202
|
+
Index 0 is self. Raises ValueError if no such node exists.
|
|
203
|
+
"""
|
|
204
|
+
if self.value == value:
|
|
205
|
+
return 0
|
|
206
|
+
pointer_save = self
|
|
207
|
+
pointer_now = self.pointer
|
|
208
|
+
i = 1
|
|
209
|
+
while True:
|
|
210
|
+
if not isinstance(pointer_now, ChainNode):
|
|
211
|
+
if pointer_now == value and pointer_now == pointer_save.value:
|
|
212
|
+
return i
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError(f"{value} not found in chain")
|
|
215
|
+
if pointer_now.value == value:
|
|
216
|
+
return i
|
|
217
|
+
pointer_now = pointer_now.pointer
|
|
218
|
+
pointer_save = pointer_save.pointer
|
|
219
|
+
i += 1
|
|
220
|
+
|
|
221
|
+
def _find_node(self, value):
|
|
222
|
+
"""Return the first node whose value equals value, or None."""
|
|
223
|
+
node = self
|
|
224
|
+
while isinstance(node, ChainNode):
|
|
225
|
+
if node.value == value:
|
|
226
|
+
return node
|
|
227
|
+
node = node.pointer
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def node_len(self, value=point_none, pointer=point_none) -> int:
|
|
231
|
+
"""
|
|
232
|
+
Return the number of nodes starting after a given position.
|
|
233
|
+
|
|
234
|
+
With no argument, count nodes after self. With pointer=xxx, count
|
|
235
|
+
starting at xxx (inclusive). With value=xxx, count starting after the
|
|
236
|
+
node holding xxx. The two keyword arguments are mutually exclusive.
|
|
237
|
+
"""
|
|
238
|
+
if pointer is not point_none and value is not point_none:
|
|
239
|
+
raise ValueError("pointer and value are mutually exclusive")
|
|
240
|
+
|
|
241
|
+
if value is not point_none:
|
|
242
|
+
node = self._find_node(value)
|
|
243
|
+
if node is None:
|
|
244
|
+
raise ValueError(f"value {value} not found in chain")
|
|
245
|
+
start = node.pointer
|
|
246
|
+
elif pointer is not point_none:
|
|
247
|
+
start = pointer
|
|
248
|
+
else:
|
|
249
|
+
start = self.pointer
|
|
250
|
+
|
|
251
|
+
i = 0
|
|
252
|
+
while isinstance(start, ChainNode):
|
|
253
|
+
start = start.pointer
|
|
254
|
+
i += 1
|
|
255
|
+
return i
|
|
256
|
+
|
|
257
|
+
def deep_len(self, value=point_none, pointer=point_none) -> int:
|
|
258
|
+
"""
|
|
259
|
+
Return the depth of a node relative to self.
|
|
260
|
+
|
|
261
|
+
Positive if the node comes after self, 0 if it is self, negative if it
|
|
262
|
+
comes before self. The two keyword arguments are mutually exclusive.
|
|
263
|
+
Raises ValueError if the node is not on the same chain.
|
|
264
|
+
"""
|
|
265
|
+
if pointer is not point_none and value is not point_none:
|
|
266
|
+
raise ValueError("pointer and value are mutually exclusive")
|
|
267
|
+
|
|
268
|
+
if value is not point_none:
|
|
269
|
+
node = self._find_node(value)
|
|
270
|
+
if node is None:
|
|
271
|
+
raise ValueError(f"value {value} not found in chain")
|
|
272
|
+
elif pointer is not point_none:
|
|
273
|
+
node = pointer
|
|
274
|
+
else:
|
|
275
|
+
raise ValueError("either pointer or value is required")
|
|
276
|
+
|
|
277
|
+
cur = self
|
|
278
|
+
depth = 0
|
|
279
|
+
while isinstance(cur, ChainNode):
|
|
280
|
+
if cur is node:
|
|
281
|
+
return depth
|
|
282
|
+
cur = cur.pointer
|
|
283
|
+
depth += 1
|
|
284
|
+
|
|
285
|
+
cur = node
|
|
286
|
+
depth = 0
|
|
287
|
+
while isinstance(cur, ChainNode):
|
|
288
|
+
if cur is self:
|
|
289
|
+
return -depth
|
|
290
|
+
cur = cur.pointer
|
|
291
|
+
depth += 1
|
|
292
|
+
|
|
293
|
+
raise ValueError("the two nodes are not in the same chain")
|