absfuyu 2.8.1__py3-none-any.whl → 3.1.0__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.
Potentially problematic release.
This version of absfuyu might be problematic. Click here for more details.
- absfuyu/__init__.py +13 -10
- absfuyu/__main__.py +55 -38
- absfuyu/config/config.json +3 -3
- absfuyu/core.py +39 -25
- absfuyu/everything.py +4 -5
- absfuyu/extensions/__init__.py +3 -2
- absfuyu/extensions/dev/__init__.py +162 -19
- absfuyu/extensions/dev/password_hash.py +11 -10
- absfuyu/extensions/dev/passwordlib.py +256 -0
- absfuyu/extensions/dev/pkglib.py +53 -57
- absfuyu/extensions/dev/project_starter.py +58 -0
- absfuyu/extensions/dev/shutdownizer.py +8 -0
- absfuyu/extensions/extra/data_analysis.py +687 -119
- absfuyu/fun/__init__.py +88 -118
- absfuyu/fun/tarot.py +32 -34
- absfuyu/game/tictactoe2.py +90 -78
- absfuyu/{collections → general}/__init__.py +14 -12
- absfuyu/{collections → general}/content.py +105 -87
- absfuyu/{collections → general}/data_extension.py +652 -172
- absfuyu/{collections → general}/generator.py +65 -4
- absfuyu/{collections → general}/human.py +28 -3
- absfuyu/pkg_data/__init__.py +14 -36
- absfuyu/pkg_data/chemistry.pkl +0 -0
- absfuyu/pkg_data/tarot.pkl +0 -0
- absfuyu/tools/converter.py +58 -31
- absfuyu/tools/obfuscator.py +4 -4
- absfuyu/tools/stats.py +4 -4
- absfuyu/tools/web.py +2 -2
- absfuyu/util/lunar.py +144 -123
- absfuyu/util/path.py +22 -3
- absfuyu/util/performance.py +101 -14
- absfuyu/version.py +93 -84
- {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/METADATA +63 -33
- absfuyu-3.1.0.dist-info/RECORD +55 -0
- {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/WHEEL +1 -1
- absfuyu-3.1.0.dist-info/entry_points.txt +2 -0
- absfuyu/pkg_data/chemistry.json +0 -6268
- absfuyu/pkg_data/tarot.json +0 -2593
- absfuyu-2.8.1.dist-info/RECORD +0 -52
- absfuyu-2.8.1.dist-info/entry_points.txt +0 -2
- {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/LICENSE +0 -0
- {absfuyu-2.8.1.dist-info → absfuyu-3.1.0.dist-info}/top_level.txt +0 -0
|
@@ -3,23 +3,37 @@ Absfuyu: Data extension
|
|
|
3
3
|
-----------------------
|
|
4
4
|
Extension for data type such as ``list``, ``str``, ``dict``, ...
|
|
5
5
|
|
|
6
|
-
Version: 1.3
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.12.3
|
|
7
|
+
Date updated: 23/01/2024 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Features:
|
|
10
10
|
---------
|
|
11
11
|
- Text
|
|
12
|
-
-
|
|
13
|
-
-
|
|
12
|
+
- ListExt
|
|
13
|
+
- DictExt
|
|
14
14
|
- IntNumber
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
------
|
|
18
|
+
>>> from absfuyu.general.data_extension import DictExt, IntNumber, ListExt, Text
|
|
15
19
|
"""
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
# Module level
|
|
19
23
|
###########################################################################
|
|
20
24
|
__all__ = [
|
|
21
|
-
|
|
22
|
-
"
|
|
25
|
+
# Main
|
|
26
|
+
"Text",
|
|
27
|
+
"IntNumber",
|
|
28
|
+
"ListExt",
|
|
29
|
+
"DictExt",
|
|
30
|
+
# Sub
|
|
31
|
+
"Pow",
|
|
32
|
+
"ListREPR",
|
|
33
|
+
"ListNoDunder",
|
|
34
|
+
"DictBoolTrue",
|
|
35
|
+
"DictBoolFalse",
|
|
36
|
+
# "DictNoDunder",
|
|
23
37
|
]
|
|
24
38
|
|
|
25
39
|
|
|
@@ -30,19 +44,117 @@ from itertools import accumulate, chain, groupby
|
|
|
30
44
|
import math
|
|
31
45
|
import operator
|
|
32
46
|
import random
|
|
33
|
-
from typing import Any, Dict, List, Optional
|
|
47
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
48
|
+
from sys import version_info as python_version
|
|
34
49
|
|
|
35
|
-
from absfuyu.
|
|
36
|
-
from absfuyu.logger import logger
|
|
50
|
+
from absfuyu.general.generator import Generator, Charset
|
|
51
|
+
from absfuyu.logger import logger, _compress_list_for_print
|
|
37
52
|
from absfuyu.util import set_min, set_min_max
|
|
38
53
|
|
|
39
54
|
|
|
55
|
+
# Function
|
|
56
|
+
###########################################################################
|
|
57
|
+
def _dict_bool(dict_object: dict, option: bool) -> Optional[dict]:
|
|
58
|
+
"""
|
|
59
|
+
Support function DictBool class
|
|
60
|
+
"""
|
|
61
|
+
out = dict()
|
|
62
|
+
for k, v in dict_object.items():
|
|
63
|
+
if v == option:
|
|
64
|
+
out[k] = v
|
|
65
|
+
if out:
|
|
66
|
+
return out
|
|
67
|
+
else:
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Sub class
|
|
72
|
+
###########################################################################
|
|
73
|
+
class Pow:
|
|
74
|
+
"""Number power by a number"""
|
|
75
|
+
|
|
76
|
+
def __init__(self, number: Union[int, float], power_by: Union[int, float]) -> None:
|
|
77
|
+
self.number = number
|
|
78
|
+
self.power_by = power_by
|
|
79
|
+
|
|
80
|
+
def __str__(self) -> str:
|
|
81
|
+
if self.power_by == 1:
|
|
82
|
+
return str(self.number)
|
|
83
|
+
else:
|
|
84
|
+
return f"{self.number}^{self.power_by}"
|
|
85
|
+
# return f"{self.__class__.__name__}({self.number}, {self.power_by})"
|
|
86
|
+
|
|
87
|
+
def __repr__(self) -> str:
|
|
88
|
+
return self.__str__()
|
|
89
|
+
|
|
90
|
+
def to_list(self) -> List[Union[int, float]]:
|
|
91
|
+
"""
|
|
92
|
+
Convert into list
|
|
93
|
+
|
|
94
|
+
:rtype: list[int | float]
|
|
95
|
+
"""
|
|
96
|
+
return [self.number] * self.power_by
|
|
97
|
+
|
|
98
|
+
def calculate(self) -> float:
|
|
99
|
+
"""
|
|
100
|
+
Calculate the ``self.number`` to the power of ``self.power_by``
|
|
101
|
+
|
|
102
|
+
:rtype: float
|
|
103
|
+
"""
|
|
104
|
+
# return self.number**self.power_by
|
|
105
|
+
return math.pow(self.number, self.power_by)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ListREPR(list):
|
|
109
|
+
"""Show ``list`` in shorter form"""
|
|
110
|
+
|
|
111
|
+
def __repr__(self) -> str:
|
|
112
|
+
return _compress_list_for_print(self, 9)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ListNoDunder(List[str]):
|
|
116
|
+
"""Use with ``object.__dir__()``"""
|
|
117
|
+
|
|
118
|
+
def __repr__(self) -> str:
|
|
119
|
+
out = [x for x in self if not x.startswith("__")]
|
|
120
|
+
return out.__repr__()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class DictBoolTrue(Dict[Any, bool]):
|
|
124
|
+
"""Only show items when ``values == True`` in ``__repr__()``"""
|
|
125
|
+
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
temp = self.copy()
|
|
128
|
+
return _dict_bool(temp, True).__repr__()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class DictBoolFalse(Dict[Any, bool]):
|
|
132
|
+
"""Only show items when ``values == False`` in ``__repr__()``"""
|
|
133
|
+
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
temp = self.copy()
|
|
136
|
+
return _dict_bool(temp, False).__repr__()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# class DictNoDunder(dict): # W.I.P
|
|
140
|
+
# """Remove dunder methods in ``__repr__()`` of dict"""
|
|
141
|
+
|
|
142
|
+
# def __repr__(self) -> str:
|
|
143
|
+
# temp = self.copy()
|
|
144
|
+
# out = dict()
|
|
145
|
+
# for k, v in temp.items():
|
|
146
|
+
# if not str(k).startswith("__"):
|
|
147
|
+
# out.__setattr__(k, v)
|
|
148
|
+
# return out.__repr__()
|
|
149
|
+
|
|
150
|
+
|
|
40
151
|
# Class
|
|
41
152
|
###########################################################################
|
|
42
153
|
class Text(str):
|
|
43
154
|
"""
|
|
44
155
|
``str`` extension
|
|
45
156
|
"""
|
|
157
|
+
|
|
46
158
|
def divide(self, string_split_size: int = 60) -> list:
|
|
47
159
|
"""
|
|
48
160
|
Divide long string into smaller size
|
|
@@ -59,7 +171,7 @@ class Text(str):
|
|
|
59
171
|
A list in which each item is a smaller
|
|
60
172
|
string with the size of ``string_split_size``
|
|
61
173
|
(need to be concaternate later)
|
|
62
|
-
|
|
174
|
+
|
|
63
175
|
|
|
64
176
|
Example:
|
|
65
177
|
--------
|
|
@@ -73,13 +185,13 @@ class Text(str):
|
|
|
73
185
|
output.append(temp[:string_split_size])
|
|
74
186
|
temp = temp[string_split_size:]
|
|
75
187
|
return output
|
|
76
|
-
|
|
188
|
+
|
|
77
189
|
def divide_with_variable(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
190
|
+
self,
|
|
191
|
+
split_size: int = 60,
|
|
192
|
+
split_var_len: int = 12,
|
|
193
|
+
custom_var_name: Optional[str] = None,
|
|
194
|
+
) -> list:
|
|
83
195
|
"""
|
|
84
196
|
Divide long string into smaller size,
|
|
85
197
|
then assign a random variable to splited
|
|
@@ -90,11 +202,11 @@ class Text(str):
|
|
|
90
202
|
split_size : int
|
|
91
203
|
Divide string every ``x`` character
|
|
92
204
|
(Default: ``60``)
|
|
93
|
-
|
|
205
|
+
|
|
94
206
|
split_var_len : int
|
|
95
207
|
Length of variable name assigned to each item
|
|
96
208
|
(Default: ``12``)
|
|
97
|
-
|
|
209
|
+
|
|
98
210
|
custom_var_name : str
|
|
99
211
|
Custom variable name when join string
|
|
100
212
|
|
|
@@ -129,30 +241,28 @@ class Text(str):
|
|
|
129
241
|
|
|
130
242
|
temp = self.divide(split_size)
|
|
131
243
|
output = []
|
|
132
|
-
|
|
244
|
+
|
|
133
245
|
# split variable
|
|
134
246
|
splt_var_len = split_var_len
|
|
135
247
|
splt_len = len(temp)
|
|
136
248
|
|
|
137
249
|
if custom_var_name is None:
|
|
138
250
|
splt_name = Generator.generate_string(
|
|
139
|
-
charset=Charset.ALPHABET,
|
|
140
|
-
size=split_var_len,
|
|
141
|
-
times=splt_len+1
|
|
251
|
+
charset=Charset.ALPHABET, size=split_var_len, times=splt_len + 1
|
|
142
252
|
)
|
|
143
253
|
for i in range(splt_len):
|
|
144
254
|
output.append(f"{splt_name[i]}='{temp[i]}'")
|
|
145
255
|
else:
|
|
146
256
|
for i in range(splt_len):
|
|
147
257
|
output.append(f"{custom_var_name}{i+1}='{temp[i]}'")
|
|
148
|
-
|
|
258
|
+
|
|
149
259
|
# joined variable
|
|
150
260
|
temp = []
|
|
151
261
|
if custom_var_name is None:
|
|
152
262
|
for i in range(splt_len):
|
|
153
263
|
if i == 0:
|
|
154
264
|
temp.append(f"{splt_name[-1]}=")
|
|
155
|
-
if
|
|
265
|
+
if i == splt_len - 1:
|
|
156
266
|
temp.append(f"{splt_name[i]}")
|
|
157
267
|
else:
|
|
158
268
|
temp.append(f"{splt_name[i]}+")
|
|
@@ -160,11 +270,11 @@ class Text(str):
|
|
|
160
270
|
for i in range(splt_len):
|
|
161
271
|
if i == 0:
|
|
162
272
|
temp.append(f"{custom_var_name}=")
|
|
163
|
-
if
|
|
273
|
+
if i == splt_len - 1:
|
|
164
274
|
temp.append(f"{custom_var_name}{i+1}")
|
|
165
275
|
else:
|
|
166
276
|
temp.append(f"{custom_var_name}{i+1}+")
|
|
167
|
-
|
|
277
|
+
|
|
168
278
|
output.append("".join(temp))
|
|
169
279
|
if custom_var_name is None:
|
|
170
280
|
output.append(splt_name[-1])
|
|
@@ -173,7 +283,6 @@ class Text(str):
|
|
|
173
283
|
|
|
174
284
|
return output
|
|
175
285
|
|
|
176
|
-
|
|
177
286
|
def analyze(self) -> dict:
|
|
178
287
|
"""
|
|
179
288
|
String analyze (count number of type of character)
|
|
@@ -184,8 +293,8 @@ class Text(str):
|
|
|
184
293
|
A dictionary contains number of digit character,
|
|
185
294
|
uppercase character, lowercase character, and
|
|
186
295
|
special character
|
|
187
|
-
|
|
188
|
-
|
|
296
|
+
|
|
297
|
+
|
|
189
298
|
Example:
|
|
190
299
|
--------
|
|
191
300
|
>>> test = Text("Random T3xt!")
|
|
@@ -195,36 +304,30 @@ class Text(str):
|
|
|
195
304
|
|
|
196
305
|
temp = self
|
|
197
306
|
|
|
198
|
-
detail = {
|
|
199
|
-
"digit": 0,
|
|
200
|
-
"uppercase": 0,
|
|
201
|
-
"lowercase": 0,
|
|
202
|
-
"other": 0
|
|
203
|
-
}
|
|
307
|
+
detail = {"digit": 0, "uppercase": 0, "lowercase": 0, "other": 0}
|
|
204
308
|
|
|
205
309
|
for x in temp:
|
|
206
|
-
if ord(x) in range(48, 58):
|
|
310
|
+
if ord(x) in range(48, 58): # num
|
|
207
311
|
detail["digit"] += 1
|
|
208
|
-
elif ord(x) in range(65, 91):
|
|
312
|
+
elif ord(x) in range(65, 91): # cap
|
|
209
313
|
detail["uppercase"] += 1
|
|
210
|
-
elif ord(x) in range(97, 123):
|
|
314
|
+
elif ord(x) in range(97, 123): # low
|
|
211
315
|
detail["lowercase"] += 1
|
|
212
316
|
else:
|
|
213
317
|
detail["other"] += 1
|
|
214
|
-
|
|
318
|
+
|
|
215
319
|
return detail
|
|
216
|
-
|
|
217
320
|
|
|
218
|
-
def reverse(self):
|
|
321
|
+
def reverse(self) -> "Text":
|
|
219
322
|
"""
|
|
220
323
|
Reverse the string
|
|
221
|
-
|
|
324
|
+
|
|
222
325
|
Returns
|
|
223
326
|
-------
|
|
224
327
|
Text
|
|
225
328
|
Reversed string
|
|
226
|
-
|
|
227
|
-
|
|
329
|
+
|
|
330
|
+
|
|
228
331
|
Example:
|
|
229
332
|
--------
|
|
230
333
|
>>> test = Text("Hello, World!")
|
|
@@ -233,7 +336,6 @@ class Text(str):
|
|
|
233
336
|
"""
|
|
234
337
|
return __class__(self[::-1])
|
|
235
338
|
|
|
236
|
-
|
|
237
339
|
def is_pangram(self) -> bool:
|
|
238
340
|
"""
|
|
239
341
|
Check if string is a pangram
|
|
@@ -255,7 +357,7 @@ class Text(str):
|
|
|
255
357
|
"""
|
|
256
358
|
Check if string is a palindrome
|
|
257
359
|
|
|
258
|
-
A palindrome is a word, verse, or sentence
|
|
360
|
+
A palindrome is a word, verse, or sentence
|
|
259
361
|
or a number that reads the same backward or forward
|
|
260
362
|
|
|
261
363
|
Returns
|
|
@@ -267,7 +369,7 @@ class Text(str):
|
|
|
267
369
|
text = self
|
|
268
370
|
# Use string slicing [start:end:step]
|
|
269
371
|
return text == text[::-1]
|
|
270
|
-
|
|
372
|
+
|
|
271
373
|
def to_hex(self, raw: bool = False) -> str:
|
|
272
374
|
r"""
|
|
273
375
|
Convert string to hex form
|
|
@@ -291,10 +393,10 @@ class Text(str):
|
|
|
291
393
|
'\\x48\\x65\\x6c\\x6c\\x6f\\x2c\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21'
|
|
292
394
|
"""
|
|
293
395
|
text = self
|
|
294
|
-
|
|
396
|
+
|
|
295
397
|
byte_str = text.encode("utf-8")
|
|
296
398
|
hex_str = byte_str.hex()
|
|
297
|
-
|
|
399
|
+
|
|
298
400
|
if not raw:
|
|
299
401
|
temp = []
|
|
300
402
|
str_len = len(hex_str)
|
|
@@ -307,7 +409,7 @@ class Text(str):
|
|
|
307
409
|
else:
|
|
308
410
|
return hex_str
|
|
309
411
|
|
|
310
|
-
def random_capslock(self, probability: int = 50):
|
|
412
|
+
def random_capslock(self, probability: int = 50) -> "Text":
|
|
311
413
|
"""
|
|
312
414
|
Randomly capslock letter in string
|
|
313
415
|
|
|
@@ -316,7 +418,7 @@ class Text(str):
|
|
|
316
418
|
probability : int
|
|
317
419
|
Probability in range [0, 100]
|
|
318
420
|
(Default: ``50``)
|
|
319
|
-
|
|
421
|
+
|
|
320
422
|
Returns
|
|
321
423
|
-------
|
|
322
424
|
Text
|
|
@@ -331,7 +433,7 @@ class Text(str):
|
|
|
331
433
|
"""
|
|
332
434
|
probability = set_min_max(probability)
|
|
333
435
|
text = self.lower()
|
|
334
|
-
|
|
436
|
+
|
|
335
437
|
temp = []
|
|
336
438
|
for x in text:
|
|
337
439
|
if random.randint(1, 100) <= probability:
|
|
@@ -340,22 +442,65 @@ class Text(str):
|
|
|
340
442
|
logger.debug(temp)
|
|
341
443
|
return __class__("".join(temp))
|
|
342
444
|
|
|
445
|
+
def reverse_capslock(self) -> "Text":
|
|
446
|
+
"""
|
|
447
|
+
Reverse capslock in string
|
|
448
|
+
|
|
449
|
+
Returns
|
|
450
|
+
-------
|
|
451
|
+
Text
|
|
452
|
+
Reversed capslock ``Text``
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
Example:
|
|
456
|
+
--------
|
|
457
|
+
>>> test = Text("Foo")
|
|
458
|
+
>>> test.reverse_capslock()
|
|
459
|
+
'fOO'
|
|
460
|
+
"""
|
|
461
|
+
temp = list(self)
|
|
462
|
+
for i, x in enumerate(temp):
|
|
463
|
+
if x.isupper():
|
|
464
|
+
temp[i] = x.lower()
|
|
465
|
+
else:
|
|
466
|
+
temp[i] = x.upper()
|
|
467
|
+
return __class__("".join(temp))
|
|
468
|
+
|
|
469
|
+
def to_list(self) -> List[str]:
|
|
470
|
+
"""
|
|
471
|
+
Convert into list
|
|
472
|
+
|
|
473
|
+
Returns
|
|
474
|
+
-------
|
|
475
|
+
list[str]
|
|
476
|
+
List of string
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
Example:
|
|
480
|
+
--------
|
|
481
|
+
>>> test = Text("test")
|
|
482
|
+
>>> test.to_list()
|
|
483
|
+
['t', 'e', 's', 't']
|
|
484
|
+
"""
|
|
485
|
+
return list(self)
|
|
486
|
+
|
|
343
487
|
|
|
344
488
|
class IntNumber(int):
|
|
345
489
|
"""
|
|
346
490
|
``int`` extension
|
|
347
491
|
"""
|
|
492
|
+
|
|
348
493
|
# convert stuff
|
|
349
494
|
def to_binary(self) -> str:
|
|
350
495
|
"""
|
|
351
496
|
Convert to binary number
|
|
352
|
-
|
|
497
|
+
|
|
353
498
|
Returns
|
|
354
499
|
-------
|
|
355
500
|
str
|
|
356
501
|
Binary number
|
|
357
|
-
|
|
358
|
-
|
|
502
|
+
|
|
503
|
+
|
|
359
504
|
Example:
|
|
360
505
|
--------
|
|
361
506
|
>>> test = IntNumber(10)
|
|
@@ -363,7 +508,7 @@ class IntNumber(int):
|
|
|
363
508
|
'1010'
|
|
364
509
|
"""
|
|
365
510
|
return format(self, "b")
|
|
366
|
-
|
|
511
|
+
|
|
367
512
|
def to_celcius_degree(self) -> float:
|
|
368
513
|
"""
|
|
369
514
|
Convert into Celcius degree as if ``self`` is Fahrenheit degree
|
|
@@ -382,7 +527,7 @@ class IntNumber(int):
|
|
|
382
527
|
"""
|
|
383
528
|
c_degree = (self - 32) / 1.8
|
|
384
529
|
return c_degree
|
|
385
|
-
|
|
530
|
+
|
|
386
531
|
def to_fahrenheit_degree(self) -> float:
|
|
387
532
|
"""
|
|
388
533
|
Convert into Fahrenheit degree as if ``self`` is Celcius degree
|
|
@@ -402,8 +547,19 @@ class IntNumber(int):
|
|
|
402
547
|
f_degree = (self * 1.8) + 32
|
|
403
548
|
return f_degree
|
|
404
549
|
|
|
405
|
-
|
|
406
550
|
# is_stuff
|
|
551
|
+
def is_even(self) -> bool:
|
|
552
|
+
"""
|
|
553
|
+
An even number is a number which divisible by 2
|
|
554
|
+
|
|
555
|
+
Returns
|
|
556
|
+
-------
|
|
557
|
+
bool
|
|
558
|
+
| ``True`` if an even number
|
|
559
|
+
| ``False`` if not an even number
|
|
560
|
+
"""
|
|
561
|
+
return self % 2 == 0
|
|
562
|
+
|
|
407
563
|
def is_prime(self) -> bool:
|
|
408
564
|
"""
|
|
409
565
|
Check if the integer is a prime number or not
|
|
@@ -420,14 +576,14 @@ class IntNumber(int):
|
|
|
420
576
|
| ``False`` if not a prime number
|
|
421
577
|
"""
|
|
422
578
|
number = self
|
|
423
|
-
|
|
424
|
-
if
|
|
579
|
+
|
|
580
|
+
if number <= 1:
|
|
425
581
|
return False
|
|
426
|
-
for i in range(2, int(math.sqrt(number)) + 1)
|
|
427
|
-
if
|
|
582
|
+
for i in range(2, int(math.sqrt(number)) + 1): # divisor range
|
|
583
|
+
if number % i == 0:
|
|
428
584
|
return False
|
|
429
585
|
return True
|
|
430
|
-
|
|
586
|
+
|
|
431
587
|
def is_twisted_prime(self) -> bool:
|
|
432
588
|
"""
|
|
433
589
|
A number is said to be twisted prime if
|
|
@@ -445,7 +601,7 @@ class IntNumber(int):
|
|
|
445
601
|
rev = self.reverse().is_prime()
|
|
446
602
|
logger.debug(f"is prime when reversed: {rev}")
|
|
447
603
|
return prime and rev
|
|
448
|
-
|
|
604
|
+
|
|
449
605
|
def is_perfect(self) -> bool:
|
|
450
606
|
"""
|
|
451
607
|
Check if integer is perfect number
|
|
@@ -494,15 +650,15 @@ class IntNumber(int):
|
|
|
494
650
|
33_550_336,
|
|
495
651
|
8_589_869_056,
|
|
496
652
|
137_438_691_328,
|
|
497
|
-
2_305_843_008_139_952_128
|
|
653
|
+
2_305_843_008_139_952_128,
|
|
498
654
|
]
|
|
499
|
-
|
|
655
|
+
|
|
500
656
|
if int(number) in perfect_number:
|
|
501
657
|
return True
|
|
502
|
-
|
|
658
|
+
|
|
503
659
|
elif int(number) < perfect_number[-1]:
|
|
504
660
|
return False
|
|
505
|
-
|
|
661
|
+
|
|
506
662
|
else:
|
|
507
663
|
# Faster way to check
|
|
508
664
|
perfect_number_index = [
|
|
@@ -551,13 +707,13 @@ class IntNumber(int):
|
|
|
551
707
|
## 82_589_933
|
|
552
708
|
]
|
|
553
709
|
for x in perfect_number_index:
|
|
554
|
-
|
|
555
|
-
perfect_number = (
|
|
710
|
+
# a perfect number have a form of (2**(n-1))*((2**n)-1)
|
|
711
|
+
perfect_number = (2 ** (x - 1)) * ((2**x) - 1)
|
|
556
712
|
if number < perfect_number:
|
|
557
713
|
return False
|
|
558
714
|
elif number == perfect_number:
|
|
559
715
|
return True
|
|
560
|
-
|
|
716
|
+
|
|
561
717
|
# Manual way when above method not working
|
|
562
718
|
# sum
|
|
563
719
|
s = 1
|
|
@@ -565,10 +721,10 @@ class IntNumber(int):
|
|
|
565
721
|
i = 2
|
|
566
722
|
while i * i <= number:
|
|
567
723
|
if number % i == 0:
|
|
568
|
-
s += +
|
|
724
|
+
s += +i + number / i
|
|
569
725
|
i += 1
|
|
570
726
|
# s == number -> perfect
|
|
571
|
-
return
|
|
727
|
+
return True if s == number and number != 1 else False
|
|
572
728
|
|
|
573
729
|
def is_narcissistic(self) -> bool:
|
|
574
730
|
"""
|
|
@@ -580,34 +736,40 @@ class IntNumber(int):
|
|
|
580
736
|
or a plus perfect number) in a given number base ``b``
|
|
581
737
|
is a number that is the sum of its own digits
|
|
582
738
|
each raised to the power of the number of digits.
|
|
583
|
-
|
|
739
|
+
|
|
584
740
|
Returns
|
|
585
741
|
-------
|
|
586
742
|
bool
|
|
587
743
|
| ``True`` if a narcissistic number
|
|
588
744
|
| ``False`` if not a narcissistic number
|
|
589
745
|
"""
|
|
590
|
-
|
|
591
|
-
|
|
746
|
+
try:
|
|
747
|
+
check = sum([int(x) ** len(str(self)) for x in str(self)])
|
|
748
|
+
return self == check
|
|
749
|
+
except:
|
|
750
|
+
return False
|
|
592
751
|
|
|
593
|
-
def reverse(self):
|
|
752
|
+
def reverse(self) -> "IntNumber":
|
|
594
753
|
"""
|
|
595
|
-
Reverse a number
|
|
596
|
-
|
|
754
|
+
Reverse a number. Reverse ``abs(number)`` if ``number < 0``
|
|
755
|
+
|
|
597
756
|
Returns
|
|
598
757
|
-------
|
|
599
758
|
IntNumber
|
|
600
759
|
Reversed number
|
|
601
|
-
|
|
602
|
-
|
|
760
|
+
|
|
761
|
+
|
|
603
762
|
Example:
|
|
604
763
|
--------
|
|
605
764
|
>>> test = IntNumber(102)
|
|
606
765
|
>>> test.reverse()
|
|
607
766
|
201
|
|
608
767
|
"""
|
|
609
|
-
|
|
610
|
-
|
|
768
|
+
number = self
|
|
769
|
+
if number <= 1:
|
|
770
|
+
number *= -1
|
|
771
|
+
return __class__(str(number)[::-1])
|
|
772
|
+
|
|
611
773
|
def is_palindromic(self) -> bool:
|
|
612
774
|
"""
|
|
613
775
|
A palindromic number (also known as a numeral palindrome
|
|
@@ -621,7 +783,7 @@ class IntNumber(int):
|
|
|
621
783
|
| ``False`` if not a palindromic number
|
|
622
784
|
"""
|
|
623
785
|
return self == self.reverse()
|
|
624
|
-
|
|
786
|
+
|
|
625
787
|
def is_palindromic_prime(self) -> bool:
|
|
626
788
|
"""
|
|
627
789
|
A palindormic prime is a number which is both palindromic and prime
|
|
@@ -633,10 +795,9 @@ class IntNumber(int):
|
|
|
633
795
|
| ``False`` if not a palindormic prime number
|
|
634
796
|
"""
|
|
635
797
|
return self.is_palindromic() and self.is_prime()
|
|
636
|
-
|
|
637
798
|
|
|
638
799
|
# calculation stuff
|
|
639
|
-
def lcm(self, with_number: int):
|
|
800
|
+
def lcm(self, with_number: int) -> "IntNumber":
|
|
640
801
|
"""
|
|
641
802
|
Least common multiple of ``self`` and ``with_number``
|
|
642
803
|
|
|
@@ -658,15 +819,41 @@ class IntNumber(int):
|
|
|
658
819
|
510
|
|
659
820
|
"""
|
|
660
821
|
return __class__((self * with_number) // math.gcd(self, with_number))
|
|
661
|
-
|
|
662
|
-
def
|
|
822
|
+
|
|
823
|
+
def gcd(self, with_number: int) -> "IntNumber":
|
|
824
|
+
"""
|
|
825
|
+
Greatest common divisor of ``self`` and ``with_number``
|
|
826
|
+
|
|
827
|
+
Parameters
|
|
828
|
+
----------
|
|
829
|
+
with_number : int
|
|
830
|
+
The number that want to find GCD with
|
|
831
|
+
|
|
832
|
+
Returns
|
|
833
|
+
-------
|
|
834
|
+
IntNumber
|
|
835
|
+
Greatest common divisor
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
Example:
|
|
839
|
+
--------
|
|
840
|
+
>>> test = IntNumber(1024)
|
|
841
|
+
>>> test.gcd(8)
|
|
842
|
+
8
|
|
843
|
+
"""
|
|
844
|
+
if python_version.micro < 9:
|
|
845
|
+
return __class__(math.gcd(self, with_number))
|
|
846
|
+
else:
|
|
847
|
+
return __class__(math.lcm(self, with_number))
|
|
848
|
+
|
|
849
|
+
def add_to_one_digit(self, master_number: bool = False) -> "IntNumber":
|
|
663
850
|
"""
|
|
664
|
-
Convert ``self`` into 1-digit number
|
|
851
|
+
Convert ``self`` into 1-digit number
|
|
665
852
|
by adding all of the digits together
|
|
666
853
|
|
|
667
854
|
Parameters
|
|
668
855
|
----------
|
|
669
|
-
master_number: bool
|
|
856
|
+
master_number : bool
|
|
670
857
|
| Break when sum = ``22`` or ``11`` (numerology)
|
|
671
858
|
| (Default: ``False``)
|
|
672
859
|
|
|
@@ -687,42 +874,222 @@ class IntNumber(int):
|
|
|
687
874
|
11
|
|
688
875
|
"""
|
|
689
876
|
number = self
|
|
877
|
+
if number < 0:
|
|
878
|
+
number *= -1
|
|
690
879
|
logger.debug(f"Current number: {number}")
|
|
691
880
|
while len(str(number)) != 1:
|
|
692
881
|
number = sum(map(int, str(number)))
|
|
693
882
|
if master_number:
|
|
694
883
|
if number == 22 or number == 11:
|
|
695
|
-
break
|
|
884
|
+
break # Master number
|
|
696
885
|
logger.debug(f"Sum after loop: {number}")
|
|
697
886
|
return __class__(number)
|
|
698
887
|
|
|
888
|
+
def divisible_list(self, short_form: bool = True) -> List[int]:
|
|
889
|
+
"""
|
|
890
|
+
A list of divisible number
|
|
891
|
+
|
|
892
|
+
Parameters
|
|
893
|
+
----------
|
|
894
|
+
short_form : bool
|
|
895
|
+
| Show divisible list in short form
|
|
896
|
+
| Normal example: ``[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]``
|
|
897
|
+
| Short form example: ``[1, 2, 4, 8, ...,128, 256, 512, 1024] [Len: 11]``
|
|
898
|
+
| (Default: ``True``)
|
|
899
|
+
|
|
900
|
+
Returns
|
|
901
|
+
-------
|
|
902
|
+
list[int]
|
|
903
|
+
A list of divisible number
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
Example:
|
|
907
|
+
--------
|
|
908
|
+
>>> test = IntNumber(1024)
|
|
909
|
+
>>> test.divisible_list(short_form=False)
|
|
910
|
+
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
911
|
+
"""
|
|
912
|
+
|
|
913
|
+
if self <= 1:
|
|
914
|
+
return [1]
|
|
915
|
+
divi_list = [x for x in range(1, int(self / 2) + 1) if self % x == 0] + [self]
|
|
916
|
+
|
|
917
|
+
if short_form:
|
|
918
|
+
return divi_list
|
|
919
|
+
# return ListREPR(divi_list) ## FIX LATER
|
|
920
|
+
return divi_list
|
|
921
|
+
|
|
922
|
+
def prime_factor(self, short_form: bool = True) -> Union[List[int], List[Pow]]:
|
|
923
|
+
"""
|
|
924
|
+
Prime factor
|
|
699
925
|
|
|
700
|
-
|
|
926
|
+
Parameters
|
|
927
|
+
----------
|
|
928
|
+
short_form : bool
|
|
929
|
+
| Show prime list in short form
|
|
930
|
+
| Normal example: ``[2, 2, 2, 3, 3]``
|
|
931
|
+
| Short form example: ``[2^3, 3^2]``
|
|
932
|
+
| (Default: ``True``)
|
|
933
|
+
|
|
934
|
+
Returns
|
|
935
|
+
-------
|
|
936
|
+
list[int] | list[Pow]
|
|
937
|
+
| List of prime number that when multiplied together == ``self``
|
|
938
|
+
| list[int]: Long form
|
|
939
|
+
| list[Pow]: Short form
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
Example:
|
|
943
|
+
--------
|
|
944
|
+
>>> test = IntNumber(1024)
|
|
945
|
+
>>> test.prime_factor()
|
|
946
|
+
[2^10]
|
|
947
|
+
|
|
948
|
+
>>> test = IntNumber(1024)
|
|
949
|
+
>>> test.prime_factor(short_form=False)
|
|
950
|
+
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
|
|
951
|
+
"""
|
|
952
|
+
# Generate list
|
|
953
|
+
factors = []
|
|
954
|
+
divisor = 2
|
|
955
|
+
number = int(self)
|
|
956
|
+
if number <= 1:
|
|
957
|
+
return [number]
|
|
958
|
+
while divisor <= number:
|
|
959
|
+
if number % divisor == 0:
|
|
960
|
+
factors.append(divisor)
|
|
961
|
+
number //= divisor # number = number // divisor
|
|
962
|
+
else:
|
|
963
|
+
divisor += 1
|
|
964
|
+
|
|
965
|
+
# Output
|
|
966
|
+
if short_form:
|
|
967
|
+
temp = dict(Counter(factors))
|
|
968
|
+
return [Pow(k, v) for k, v in temp.items()]
|
|
969
|
+
return factors
|
|
970
|
+
|
|
971
|
+
# analyze
|
|
972
|
+
def analyze(self, short_form: bool = True) -> Dict[str, Dict[str, Any]]:
|
|
973
|
+
"""
|
|
974
|
+
Analyze the number with almost all ``IntNumber`` method
|
|
975
|
+
|
|
976
|
+
Parameters
|
|
977
|
+
----------
|
|
978
|
+
short_form : bool
|
|
979
|
+
| Enable short form for some items
|
|
980
|
+
| (Default: ``True``)
|
|
981
|
+
|
|
982
|
+
Returns
|
|
983
|
+
-------
|
|
984
|
+
dict[str, dict[str, Any]]
|
|
985
|
+
Detailed analysis
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
Example:
|
|
989
|
+
--------
|
|
990
|
+
>>> test = IntNumber(1024)
|
|
991
|
+
>>> test.analyze()
|
|
992
|
+
{
|
|
993
|
+
'summary': {'number': 1024, 'length': 4, 'even': True, 'prime factor': [2^10], 'divisible': [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]},
|
|
994
|
+
'convert': {'binary': '10000000000', 'octa': '2000', 'hex': '400', 'reverse': 4201, 'add to one': 7},
|
|
995
|
+
'characteristic': {'prime': False, 'twisted prime': False, 'perfect': False, 'narcissistic': False, 'palindromic': False, 'palindromic prime': False}
|
|
996
|
+
}
|
|
997
|
+
"""
|
|
998
|
+
output = {
|
|
999
|
+
"summary": {
|
|
1000
|
+
"number": self,
|
|
1001
|
+
"length": len(str(self)),
|
|
1002
|
+
"even": self.is_even(),
|
|
1003
|
+
"prime factor": self.prime_factor(short_form=short_form),
|
|
1004
|
+
"divisible": self.divisible_list(short_form=short_form),
|
|
1005
|
+
},
|
|
1006
|
+
"convert": {
|
|
1007
|
+
"binary": bin(self)[2:],
|
|
1008
|
+
"octa": oct(self)[2:],
|
|
1009
|
+
"hex": hex(self)[2:],
|
|
1010
|
+
# "hash": hash(self),
|
|
1011
|
+
"reverse": self.reverse(),
|
|
1012
|
+
"add to one": self.add_to_one_digit(),
|
|
1013
|
+
},
|
|
1014
|
+
}
|
|
1015
|
+
characteristic = {
|
|
1016
|
+
"prime": self.is_prime(),
|
|
1017
|
+
"twisted prime": self.is_twisted_prime(),
|
|
1018
|
+
"perfect": self.is_perfect(),
|
|
1019
|
+
"narcissistic": self.is_narcissistic(),
|
|
1020
|
+
"palindromic": self.is_palindromic(),
|
|
1021
|
+
"palindromic prime": self.is_palindromic_prime(),
|
|
1022
|
+
}
|
|
1023
|
+
if short_form:
|
|
1024
|
+
characteristic = DictBoolTrue(characteristic)
|
|
1025
|
+
|
|
1026
|
+
output["characteristic"] = characteristic
|
|
1027
|
+
return output
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
class ListExt(list):
|
|
701
1031
|
"""
|
|
702
1032
|
``list`` extension
|
|
703
1033
|
"""
|
|
704
|
-
|
|
1034
|
+
|
|
1035
|
+
def stringify(self) -> "ListExt":
|
|
705
1036
|
"""
|
|
706
1037
|
Convert all item in ``list`` into string
|
|
707
1038
|
|
|
708
1039
|
Returns
|
|
709
1040
|
-------
|
|
710
|
-
|
|
711
|
-
A list with all items with type
|
|
1041
|
+
ListExt
|
|
1042
|
+
A list with all items with type <str`>
|
|
712
1043
|
|
|
713
1044
|
|
|
714
1045
|
Example:
|
|
715
1046
|
--------
|
|
716
|
-
>>> test =
|
|
1047
|
+
>>> test = ListExt([1, 1, 1, 2, 2, 3])
|
|
717
1048
|
>>> test.stringify()
|
|
718
1049
|
['1', '1', '1', '2', '2', '3']
|
|
719
1050
|
"""
|
|
720
|
-
return
|
|
1051
|
+
return ListExt(map(str, self))
|
|
1052
|
+
|
|
1053
|
+
def head(self, number_of_items: int = 5) -> list:
|
|
1054
|
+
"""
|
|
1055
|
+
Show first ``number_of_items`` items in ``ListExt``
|
|
1056
|
+
|
|
1057
|
+
Parameters
|
|
1058
|
+
----------
|
|
1059
|
+
number_of_items : int
|
|
1060
|
+
| Number of items to shows at once
|
|
1061
|
+
| (Default: ``5``)
|
|
721
1062
|
|
|
722
|
-
|
|
1063
|
+
Returns
|
|
1064
|
+
-------
|
|
1065
|
+
list
|
|
1066
|
+
Filtered list
|
|
1067
|
+
"""
|
|
1068
|
+
number_of_items = set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1069
|
+
return self[:number_of_items]
|
|
1070
|
+
|
|
1071
|
+
def tail(self, number_of_items: int = 5) -> list:
|
|
1072
|
+
"""
|
|
1073
|
+
Show last ``number_of_items`` items in ``ListExt``
|
|
1074
|
+
|
|
1075
|
+
Parameters
|
|
1076
|
+
----------
|
|
1077
|
+
number_of_items : int
|
|
1078
|
+
| Number of items to shows at once
|
|
1079
|
+
| (Default: ``5``)
|
|
1080
|
+
|
|
1081
|
+
Returns
|
|
1082
|
+
-------
|
|
1083
|
+
list
|
|
1084
|
+
Filtered list
|
|
1085
|
+
"""
|
|
1086
|
+
number_of_items = set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1087
|
+
return self[::-1][:number_of_items][::-1]
|
|
1088
|
+
|
|
1089
|
+
def sorts(self, reverse: bool = False) -> "ListExt":
|
|
723
1090
|
"""
|
|
724
1091
|
Sort all items (with different type) in ``list``
|
|
725
|
-
|
|
1092
|
+
|
|
726
1093
|
Parameters
|
|
727
1094
|
----------
|
|
728
1095
|
reverse : bool
|
|
@@ -732,13 +1099,13 @@ class ListKai(list):
|
|
|
732
1099
|
|
|
733
1100
|
Returns
|
|
734
1101
|
-------
|
|
735
|
-
|
|
1102
|
+
ListExt
|
|
736
1103
|
A sorted list
|
|
737
1104
|
|
|
738
1105
|
|
|
739
1106
|
Example:
|
|
740
1107
|
--------
|
|
741
|
-
>>> test =
|
|
1108
|
+
>>> test = ListExt([9, "abc", 3.5, "aaa", 1, 1.4])
|
|
742
1109
|
>>> test.sorts()
|
|
743
1110
|
[1, 9, 'aaa', 'abc', 1.4, 3.5]
|
|
744
1111
|
"""
|
|
@@ -750,20 +1117,18 @@ class ListKai(list):
|
|
|
750
1117
|
logger.debug(f"Type weight: {type_weights}")
|
|
751
1118
|
|
|
752
1119
|
output = sorted(
|
|
753
|
-
lst,
|
|
754
|
-
key=lambda x: (type_weights[type(x)], str(x)),
|
|
755
|
-
reverse=reverse
|
|
1120
|
+
lst, key=lambda x: (type_weights[type(x)], str(x)), reverse=reverse
|
|
756
1121
|
)
|
|
757
1122
|
|
|
758
1123
|
logger.debug(output)
|
|
759
1124
|
return __class__(output)
|
|
760
|
-
|
|
1125
|
+
|
|
761
1126
|
def freq(
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1127
|
+
self,
|
|
1128
|
+
sort: bool = False,
|
|
1129
|
+
num_of_first_char: Optional[int] = None,
|
|
1130
|
+
appear_increment: bool = False,
|
|
1131
|
+
):
|
|
767
1132
|
"""
|
|
768
1133
|
Find frequency of each item in list
|
|
769
1134
|
|
|
@@ -772,12 +1137,12 @@ class ListKai(list):
|
|
|
772
1137
|
sort : bool
|
|
773
1138
|
| if ``True``: Sorts the output in ascending order
|
|
774
1139
|
| if ``False``: No sort
|
|
775
|
-
|
|
1140
|
+
|
|
776
1141
|
num_of_first_char : int | None
|
|
777
1142
|
| Number of first character taken into account to sort
|
|
778
1143
|
| (Default: None)
|
|
779
1144
|
| (num_of_first_char = ``1``: first character in each item)
|
|
780
|
-
|
|
1145
|
+
|
|
781
1146
|
appear_increment : bool
|
|
782
1147
|
| return incremental index list of each item when sort
|
|
783
1148
|
| (Default: ``False``)
|
|
@@ -786,18 +1151,18 @@ class ListKai(list):
|
|
|
786
1151
|
-------
|
|
787
1152
|
dict
|
|
788
1153
|
A dict that show frequency
|
|
789
|
-
|
|
1154
|
+
|
|
790
1155
|
list
|
|
791
1156
|
Incremental index list
|
|
792
|
-
|
|
1157
|
+
|
|
793
1158
|
|
|
794
1159
|
Example:
|
|
795
1160
|
--------
|
|
796
|
-
>>> test =
|
|
1161
|
+
>>> test = ListExt([1, 1, 2, 3, 5, 5])
|
|
797
1162
|
>>> test.freq()
|
|
798
1163
|
{1: 2, 2: 1, 3: 1, 5: 2}
|
|
799
1164
|
|
|
800
|
-
>>> test =
|
|
1165
|
+
>>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
|
|
801
1166
|
>>> test.freq(appear_increment=True)
|
|
802
1167
|
[2, 3, 5, 6, 7, 8]
|
|
803
1168
|
"""
|
|
@@ -806,7 +1171,7 @@ class ListKai(list):
|
|
|
806
1171
|
data = self.sorts()
|
|
807
1172
|
else:
|
|
808
1173
|
data = self.copy()
|
|
809
|
-
|
|
1174
|
+
|
|
810
1175
|
if num_of_first_char is None:
|
|
811
1176
|
temp = Counter(data)
|
|
812
1177
|
else:
|
|
@@ -818,21 +1183,23 @@ class ListKai(list):
|
|
|
818
1183
|
else:
|
|
819
1184
|
logger.debug(f"Freq of first {num_of_first_char} char")
|
|
820
1185
|
temp = Counter([str(x)[:num_of_first_char] for x in data])
|
|
821
|
-
|
|
1186
|
+
|
|
822
1187
|
try:
|
|
823
1188
|
times_appear = dict(sorted(temp.items()))
|
|
824
1189
|
except:
|
|
825
1190
|
times_appear = dict(__class__(temp.items()).sorts())
|
|
826
1191
|
logger.debug(times_appear)
|
|
827
|
-
|
|
1192
|
+
|
|
828
1193
|
if appear_increment:
|
|
829
|
-
times_appear_increment = list(
|
|
1194
|
+
times_appear_increment = list(
|
|
1195
|
+
accumulate(times_appear.values(), operator.add)
|
|
1196
|
+
)
|
|
830
1197
|
logger.debug(times_appear_increment)
|
|
831
1198
|
return times_appear_increment
|
|
832
1199
|
else:
|
|
833
1200
|
return times_appear
|
|
834
1201
|
|
|
835
|
-
def slice_points(self, points: list):
|
|
1202
|
+
def slice_points(self, points: list) -> List[list]:
|
|
836
1203
|
"""
|
|
837
1204
|
Slices a list at specific indices into constituent lists.
|
|
838
1205
|
|
|
@@ -840,7 +1207,7 @@ class ListKai(list):
|
|
|
840
1207
|
----------
|
|
841
1208
|
points : list
|
|
842
1209
|
List of indices to slice
|
|
843
|
-
|
|
1210
|
+
|
|
844
1211
|
Returns
|
|
845
1212
|
-------
|
|
846
1213
|
list[list]
|
|
@@ -849,28 +1216,28 @@ class ListKai(list):
|
|
|
849
1216
|
|
|
850
1217
|
Example:
|
|
851
1218
|
--------
|
|
852
|
-
>>> test =
|
|
1219
|
+
>>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
|
|
853
1220
|
>>> test.slice_points([2, 5])
|
|
854
1221
|
[[1, 1], [2, 3, 3], [4, 5, 6]]
|
|
855
1222
|
"""
|
|
856
1223
|
points.append(len(self))
|
|
857
1224
|
data = self.copy()
|
|
858
1225
|
# return [data[points[i]:points[i+1]] for i in range(len(points)-1)]
|
|
859
|
-
return [data[i1:i2] for i1, i2 in zip([0]+points[:-1], points)]
|
|
1226
|
+
return [data[i1:i2] for i1, i2 in zip([0] + points[:-1], points)]
|
|
860
1227
|
|
|
861
|
-
def pick_one(self):
|
|
1228
|
+
def pick_one(self) -> Any:
|
|
862
1229
|
"""
|
|
863
1230
|
Pick one random items from ``list``
|
|
864
|
-
|
|
1231
|
+
|
|
865
1232
|
Returns
|
|
866
1233
|
-------
|
|
867
|
-
Any
|
|
1234
|
+
Any
|
|
868
1235
|
Random value
|
|
869
1236
|
|
|
870
1237
|
|
|
871
1238
|
Example:
|
|
872
1239
|
--------
|
|
873
|
-
>>> test =
|
|
1240
|
+
>>> test = ListExt(["foo", "bar"])
|
|
874
1241
|
>>> test.pick_one()
|
|
875
1242
|
'bar'
|
|
876
1243
|
"""
|
|
@@ -880,33 +1247,50 @@ class ListKai(list):
|
|
|
880
1247
|
return out
|
|
881
1248
|
else:
|
|
882
1249
|
logger.debug("List empty!")
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
def
|
|
1250
|
+
raise IndexError("List empty!")
|
|
1251
|
+
|
|
1252
|
+
def get_random(self, number_of_items: int = 5) -> list:
|
|
1253
|
+
"""
|
|
1254
|
+
Get ``number_of_items`` random items in ``ListExt``
|
|
1255
|
+
|
|
1256
|
+
Parameters
|
|
1257
|
+
----------
|
|
1258
|
+
number_of_items : int
|
|
1259
|
+
| Number random of items
|
|
1260
|
+
| (Default: ``5``)
|
|
1261
|
+
|
|
1262
|
+
Returns
|
|
1263
|
+
-------
|
|
1264
|
+
list
|
|
1265
|
+
Filtered list
|
|
1266
|
+
"""
|
|
1267
|
+
return [self.pick_one() for _ in range(number_of_items)]
|
|
1268
|
+
|
|
1269
|
+
def len_items(self) -> "ListExt":
|
|
886
1270
|
"""
|
|
887
1271
|
``len()`` for every item in ``list[str]``
|
|
888
1272
|
|
|
889
1273
|
Returns
|
|
890
1274
|
-------
|
|
891
|
-
|
|
1275
|
+
ListExt
|
|
892
1276
|
List of ``len()``'ed value
|
|
893
1277
|
|
|
894
1278
|
|
|
895
1279
|
Example:
|
|
896
1280
|
--------
|
|
897
|
-
>>> test =
|
|
1281
|
+
>>> test = ListExt(["foo", "bar", "pizza"])
|
|
898
1282
|
>>> test.len_items()
|
|
899
1283
|
[3, 3, 5]
|
|
900
1284
|
"""
|
|
901
|
-
out =
|
|
902
|
-
# out =
|
|
1285
|
+
out = ListExt([len(str(x)) for x in self])
|
|
1286
|
+
# out = ListExt(map(lambda x: len(str(x)), self))
|
|
903
1287
|
logger.debug(out)
|
|
904
1288
|
return out
|
|
905
1289
|
|
|
906
1290
|
def mean_len(self) -> float:
|
|
907
1291
|
"""
|
|
908
1292
|
Average length of every item
|
|
909
|
-
|
|
1293
|
+
|
|
910
1294
|
Returns
|
|
911
1295
|
-------
|
|
912
1296
|
float
|
|
@@ -915,69 +1299,69 @@ class ListKai(list):
|
|
|
915
1299
|
|
|
916
1300
|
Example:
|
|
917
1301
|
--------
|
|
918
|
-
>>> test =
|
|
1302
|
+
>>> test = ListExt(["foo", "bar", "pizza"])
|
|
919
1303
|
>>> test.mean_len()
|
|
920
1304
|
3.6666666666666665
|
|
921
1305
|
"""
|
|
922
|
-
out = sum(self.len_items())/len(self)
|
|
1306
|
+
out = sum(self.len_items()) / len(self)
|
|
923
1307
|
logger.debug(out)
|
|
924
1308
|
return out
|
|
925
|
-
|
|
926
|
-
def apply(self, func):
|
|
1309
|
+
|
|
1310
|
+
def apply(self, func: Callable) -> "ListExt":
|
|
927
1311
|
"""
|
|
928
1312
|
Apply function to each entry
|
|
929
|
-
|
|
1313
|
+
|
|
930
1314
|
Parameters
|
|
931
1315
|
----------
|
|
932
1316
|
func : Callable
|
|
933
1317
|
Callable function
|
|
934
|
-
|
|
1318
|
+
|
|
935
1319
|
Returns
|
|
936
1320
|
-------
|
|
937
|
-
|
|
938
|
-
|
|
1321
|
+
ListExt
|
|
1322
|
+
ListExt
|
|
939
1323
|
|
|
940
1324
|
|
|
941
1325
|
Example:
|
|
942
1326
|
--------
|
|
943
|
-
>>> test =
|
|
1327
|
+
>>> test = ListExt([1, 2, 3])
|
|
944
1328
|
>>> test.apply(str)
|
|
945
1329
|
['1', '2', '3']
|
|
946
1330
|
"""
|
|
947
1331
|
# return __class__(func(x) for x in self)
|
|
948
1332
|
return __class__(map(func, self))
|
|
949
1333
|
|
|
950
|
-
def unique(self):
|
|
1334
|
+
def unique(self) -> "ListExt":
|
|
951
1335
|
"""
|
|
952
1336
|
Remove duplicates
|
|
953
1337
|
|
|
954
1338
|
Returns
|
|
955
1339
|
-------
|
|
956
|
-
|
|
1340
|
+
ListExt
|
|
957
1341
|
Duplicates removed list
|
|
958
|
-
|
|
959
|
-
|
|
1342
|
+
|
|
1343
|
+
|
|
960
1344
|
Example:
|
|
961
1345
|
--------
|
|
962
|
-
>>> test =
|
|
1346
|
+
>>> test = ListExt([1, 1, 1, 2, 2, 3])
|
|
963
1347
|
>>> test.unique()
|
|
964
1348
|
[1, 2, 3]
|
|
965
1349
|
"""
|
|
966
1350
|
return __class__(set(self))
|
|
967
|
-
|
|
968
|
-
def group_by_unique(self):
|
|
1351
|
+
|
|
1352
|
+
def group_by_unique(self) -> "ListExt":
|
|
969
1353
|
"""
|
|
970
1354
|
Group duplicated elements into list
|
|
971
1355
|
|
|
972
1356
|
Returns
|
|
973
1357
|
-------
|
|
974
|
-
|
|
1358
|
+
ListExt
|
|
975
1359
|
Grouped value
|
|
976
|
-
|
|
1360
|
+
|
|
977
1361
|
|
|
978
1362
|
Example:
|
|
979
1363
|
--------
|
|
980
|
-
>>> test =
|
|
1364
|
+
>>> test = ListExt([1, 2, 3, 1, 3, 3, 2])
|
|
981
1365
|
>>> test.group_by_unique()
|
|
982
1366
|
[[1, 1], [2, 2], [3, 3, 3]]
|
|
983
1367
|
"""
|
|
@@ -989,33 +1373,40 @@ class ListKai(list):
|
|
|
989
1373
|
temp = groupby(self.sorts())
|
|
990
1374
|
return __class__([list(g) for _, g in temp])
|
|
991
1375
|
|
|
1376
|
+
@staticmethod
|
|
1377
|
+
def _group_by_unique(iterable: list) -> List[list]:
|
|
1378
|
+
"""
|
|
1379
|
+
Static method for ``group_by_unique``
|
|
1380
|
+
"""
|
|
1381
|
+
return list([list(g) for _, g in groupby(iterable)])
|
|
1382
|
+
|
|
992
1383
|
def group_by_pair_value(self, max_loop: int = 3) -> List[list]:
|
|
993
1384
|
"""
|
|
994
|
-
Assume each ``list`` in ``list`` is a pair value,
|
|
1385
|
+
Assume each ``list`` in ``list`` is a pair value,
|
|
995
1386
|
returns a ``list`` contain all paired value
|
|
996
1387
|
|
|
997
1388
|
Parameters
|
|
998
1389
|
----------
|
|
999
1390
|
max_loop : int
|
|
1000
1391
|
Times to run functions (minimum: ``3``)
|
|
1001
|
-
|
|
1392
|
+
|
|
1002
1393
|
Returns
|
|
1003
1394
|
-------
|
|
1004
1395
|
list[list]
|
|
1005
1396
|
Grouped value
|
|
1006
|
-
|
|
1397
|
+
|
|
1007
1398
|
|
|
1008
1399
|
Example:
|
|
1009
1400
|
--------
|
|
1010
|
-
>>> test =
|
|
1401
|
+
>>> test = ListExt([[1, 2], [2, 3], [4, 3], [5, 6]])
|
|
1011
1402
|
>>> test.group_by_pair_value()
|
|
1012
1403
|
[[1, 2, 3, 4], [5, 6]]
|
|
1013
1404
|
|
|
1014
|
-
>>> test =
|
|
1405
|
+
>>> test = ListExt([[8, 3], [4, 6], [6, 3], [5, 2], [7, 2]])
|
|
1015
1406
|
>>> test.group_by_pair_value()
|
|
1016
1407
|
[[8, 3, 4, 6], [2, 5, 7]]
|
|
1017
1408
|
|
|
1018
|
-
>>> test =
|
|
1409
|
+
>>> test = ListExt([["a", 4], ["b", 4], [5, "c"]])
|
|
1019
1410
|
>>> test.group_by_pair_value()
|
|
1020
1411
|
[['a', 4, 'b'], ['c', 5]]
|
|
1021
1412
|
"""
|
|
@@ -1024,7 +1415,6 @@ class ListKai(list):
|
|
|
1024
1415
|
|
|
1025
1416
|
# Init loop
|
|
1026
1417
|
for _ in range(set_min(max_loop, min_value=3)):
|
|
1027
|
-
|
|
1028
1418
|
temp: Dict[Any, list] = {}
|
|
1029
1419
|
# Make dict{key: all `item` that contains `key`}
|
|
1030
1420
|
for item in iter:
|
|
@@ -1037,19 +1427,76 @@ class ListKai(list):
|
|
|
1037
1427
|
# Flatten dict.values
|
|
1038
1428
|
for k, v in temp.items():
|
|
1039
1429
|
temp[k] = list(set(chain(*v)))
|
|
1040
|
-
|
|
1430
|
+
|
|
1041
1431
|
iter = list(temp.values())
|
|
1042
1432
|
|
|
1043
1433
|
return list(x for x, _ in groupby(iter))
|
|
1044
1434
|
|
|
1435
|
+
def flatten(self) -> "ListExt":
|
|
1436
|
+
"""
|
|
1437
|
+
Flatten the list
|
|
1438
|
+
|
|
1439
|
+
Returns
|
|
1440
|
+
-------
|
|
1441
|
+
ListExt
|
|
1442
|
+
Flattened list
|
|
1443
|
+
|
|
1045
1444
|
|
|
1046
|
-
|
|
1445
|
+
Example:
|
|
1446
|
+
--------
|
|
1447
|
+
>>> test = ListExt([["test"], ["test", "test"], ["test"]])
|
|
1448
|
+
>>> test.flatten()
|
|
1449
|
+
['test', 'test', 'test', 'test']
|
|
1450
|
+
"""
|
|
1451
|
+
try:
|
|
1452
|
+
return ListExt(chain(*self))
|
|
1453
|
+
except:
|
|
1454
|
+
temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
|
|
1455
|
+
return ListExt(chain(*temp))
|
|
1456
|
+
|
|
1457
|
+
def numbering(self, start: int = 0) -> "ListExt":
|
|
1458
|
+
"""
|
|
1459
|
+
Number the item in list
|
|
1460
|
+
(``enumerate`` wrapper)
|
|
1461
|
+
|
|
1462
|
+
Parameters
|
|
1463
|
+
----------
|
|
1464
|
+
start : int
|
|
1465
|
+
Start from which number
|
|
1466
|
+
(Default: ``0``)
|
|
1467
|
+
|
|
1468
|
+
Returns
|
|
1469
|
+
-------
|
|
1470
|
+
ListExt
|
|
1471
|
+
Counted list
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
Example:
|
|
1475
|
+
--------
|
|
1476
|
+
>>> test = ListExt([9, 9, 9])
|
|
1477
|
+
>>> test.numbering()
|
|
1478
|
+
[(0, 9), (1, 9), (2, 9)]
|
|
1479
|
+
"""
|
|
1480
|
+
start = set_min(start, min_value=0)
|
|
1481
|
+
return ListExt(enumerate(self, start=start))
|
|
1482
|
+
|
|
1483
|
+
@staticmethod
|
|
1484
|
+
def _numbering(iterable: list, start: int = 0) -> List[Tuple[int, Any]]:
|
|
1485
|
+
"""
|
|
1486
|
+
Static method for ``numbering``
|
|
1487
|
+
"""
|
|
1488
|
+
start = set_min(start, min_value=0)
|
|
1489
|
+
return list(enumerate(iterable, start=start))
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
class DictExt(dict):
|
|
1047
1493
|
"""
|
|
1048
1494
|
``dict`` extension
|
|
1049
1495
|
"""
|
|
1496
|
+
|
|
1050
1497
|
def analyze(self) -> dict:
|
|
1051
1498
|
"""
|
|
1052
|
-
Analyze all the key values (``int``, ``float``)
|
|
1499
|
+
Analyze all the key values (``int``, ``float``)
|
|
1053
1500
|
in ``dict`` then return highest/lowest index
|
|
1054
1501
|
|
|
1055
1502
|
Returns
|
|
@@ -1060,7 +1507,7 @@ class DictKai(dict):
|
|
|
1060
1507
|
|
|
1061
1508
|
Example:
|
|
1062
1509
|
--------
|
|
1063
|
-
>>> test =
|
|
1510
|
+
>>> test = DictExt({
|
|
1064
1511
|
>>> "abc": 9,
|
|
1065
1512
|
>>> "def": 9,
|
|
1066
1513
|
>>> "ghi": 8,
|
|
@@ -1093,38 +1540,71 @@ class DictKai(dict):
|
|
|
1093
1540
|
"max_value": max_val,
|
|
1094
1541
|
"min_value": min_val,
|
|
1095
1542
|
"max": max_list,
|
|
1096
|
-
"min": min_list
|
|
1543
|
+
"min": min_list,
|
|
1097
1544
|
}
|
|
1098
1545
|
|
|
1099
1546
|
logger.debug(output)
|
|
1100
1547
|
return output
|
|
1101
|
-
|
|
1548
|
+
|
|
1102
1549
|
except:
|
|
1103
1550
|
err_msg = "Value must be int or float"
|
|
1104
1551
|
logger.error(err_msg)
|
|
1105
1552
|
raise ValueError(err_msg)
|
|
1106
|
-
|
|
1107
|
-
def swap_items(self):
|
|
1553
|
+
|
|
1554
|
+
def swap_items(self) -> "DictExt":
|
|
1108
1555
|
"""
|
|
1109
1556
|
Swap ``dict.keys()`` with ``dict.values()``
|
|
1110
1557
|
|
|
1111
1558
|
Returns
|
|
1112
1559
|
-------
|
|
1113
|
-
|
|
1560
|
+
DictExt
|
|
1114
1561
|
Swapped dict
|
|
1115
1562
|
|
|
1116
1563
|
|
|
1117
1564
|
Example:
|
|
1118
1565
|
--------
|
|
1119
|
-
>>> test =
|
|
1566
|
+
>>> test = DictExt({"abc": 9})
|
|
1120
1567
|
>>> test.swap_items()
|
|
1121
1568
|
{9: 'abc'}
|
|
1122
1569
|
"""
|
|
1123
1570
|
return __class__(zip(self.values(), self.keys()))
|
|
1124
1571
|
|
|
1572
|
+
def apply(self, func: Callable, apply_to_value: bool = True) -> "DictExt":
|
|
1573
|
+
"""
|
|
1574
|
+
Apply function to ``DictExt.keys()`` or ``DictExt.values()``
|
|
1575
|
+
|
|
1576
|
+
Parameters
|
|
1577
|
+
----------
|
|
1578
|
+
func : Callable
|
|
1579
|
+
Callable function
|
|
1580
|
+
|
|
1581
|
+
apply_to_value : bool
|
|
1582
|
+
| ``True``: Apply ``func`` to ``DictExt.values()``
|
|
1583
|
+
| ``False``: Apply ``func`` to ``DictExt.keys()``
|
|
1584
|
+
|
|
1585
|
+
Returns
|
|
1586
|
+
-------
|
|
1587
|
+
DictExt
|
|
1588
|
+
DictExt
|
|
1589
|
+
|
|
1590
|
+
|
|
1591
|
+
Example:
|
|
1592
|
+
--------
|
|
1593
|
+
>>> test = DictExt({"abc": 9})
|
|
1594
|
+
>>> test.apply(str)
|
|
1595
|
+
{'abc': '9'}
|
|
1596
|
+
"""
|
|
1597
|
+
if apply_to_value:
|
|
1598
|
+
k = self.keys()
|
|
1599
|
+
v = map(func, self.values())
|
|
1600
|
+
else:
|
|
1601
|
+
k = map(func, self.keys())
|
|
1602
|
+
v = self.values()
|
|
1603
|
+
return __class__(zip(k, v))
|
|
1125
1604
|
|
|
1126
1605
|
|
|
1127
1606
|
# Run
|
|
1128
1607
|
###########################################################################
|
|
1129
1608
|
if __name__ == "__main__":
|
|
1130
1609
|
logger.setLevel(10)
|
|
1610
|
+
# from rich import print
|