absfuyu 3.2.0__py3-none-any.whl → 3.3.3__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 +3 -10
- absfuyu/__main__.py +5 -250
- absfuyu/cli/__init__.py +51 -0
- absfuyu/cli/color.py +24 -0
- absfuyu/cli/config_group.py +56 -0
- absfuyu/cli/do_group.py +76 -0
- absfuyu/cli/game_group.py +109 -0
- absfuyu/config/__init__.py +55 -94
- absfuyu/config/config.json +0 -7
- absfuyu/core.py +5 -66
- absfuyu/everything.py +7 -9
- absfuyu/extensions/beautiful.py +30 -23
- absfuyu/extensions/dev/__init__.py +11 -8
- absfuyu/extensions/dev/password_hash.py +4 -2
- absfuyu/extensions/dev/passwordlib.py +7 -5
- absfuyu/extensions/dev/project_starter.py +4 -2
- absfuyu/extensions/dev/shutdownizer.py +148 -0
- absfuyu/extensions/extra/__init__.py +1 -2
- absfuyu/extensions/extra/data_analysis.py +110 -58
- absfuyu/fun/WGS.py +50 -26
- absfuyu/fun/__init__.py +6 -7
- absfuyu/fun/tarot.py +1 -1
- absfuyu/game/__init__.py +75 -81
- absfuyu/game/game_stat.py +36 -0
- absfuyu/game/sudoku.py +41 -48
- absfuyu/game/tictactoe.py +303 -548
- absfuyu/game/wordle.py +56 -47
- absfuyu/general/__init__.py +17 -7
- absfuyu/general/content.py +16 -15
- absfuyu/general/data_extension.py +282 -90
- absfuyu/general/generator.py +67 -67
- absfuyu/general/human.py +74 -78
- absfuyu/logger.py +94 -68
- absfuyu/pkg_data/__init__.py +29 -25
- absfuyu/py.typed +0 -0
- absfuyu/sort.py +61 -47
- absfuyu/tools/__init__.py +0 -1
- absfuyu/tools/converter.py +80 -62
- absfuyu/tools/keygen.py +62 -67
- absfuyu/tools/obfuscator.py +57 -53
- absfuyu/tools/stats.py +24 -24
- absfuyu/tools/web.py +10 -9
- absfuyu/util/__init__.py +38 -40
- absfuyu/util/api.py +53 -43
- absfuyu/util/json_method.py +25 -27
- absfuyu/util/lunar.py +20 -24
- absfuyu/util/path.py +362 -241
- absfuyu/util/performance.py +36 -98
- absfuyu/util/pkl.py +8 -8
- absfuyu/util/zipped.py +17 -19
- absfuyu/version.py +137 -148
- absfuyu-3.3.3.dist-info/METADATA +124 -0
- absfuyu-3.3.3.dist-info/RECORD +59 -0
- {absfuyu-3.2.0.dist-info → absfuyu-3.3.3.dist-info}/WHEEL +1 -2
- {absfuyu-3.2.0.dist-info → absfuyu-3.3.3.dist-info}/entry_points.txt +1 -0
- {absfuyu-3.2.0.dist-info → absfuyu-3.3.3.dist-info/licenses}/LICENSE +1 -1
- absfuyu/extensions/dev/pkglib.py +0 -98
- absfuyu/game/tictactoe2.py +0 -318
- absfuyu-3.2.0.dist-info/METADATA +0 -216
- absfuyu-3.2.0.dist-info/RECORD +0 -55
- absfuyu-3.2.0.dist-info/top_level.txt +0 -1
|
@@ -3,22 +3,21 @@ Absfuyu: Data extension
|
|
|
3
3
|
-----------------------
|
|
4
4
|
Extension for data type such as ``list``, ``str``, ``dict``, ...
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.14.3
|
|
7
|
+
Date updated: 05/04/2024 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Features:
|
|
10
10
|
---------
|
|
11
|
-
- Text
|
|
12
|
-
- ListExt
|
|
13
11
|
- DictExt
|
|
14
12
|
- IntNumber
|
|
13
|
+
- ListExt
|
|
14
|
+
- Text
|
|
15
15
|
|
|
16
16
|
Usage:
|
|
17
17
|
------
|
|
18
18
|
>>> from absfuyu.general.data_extension import DictExt, IntNumber, ListExt, Text
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
# Module level
|
|
23
22
|
###########################################################################
|
|
24
23
|
__all__ = [
|
|
@@ -34,21 +33,42 @@ __all__ = [
|
|
|
34
33
|
"DictBoolTrue",
|
|
35
34
|
"DictBoolFalse",
|
|
36
35
|
# "DictNoDunder",
|
|
36
|
+
# Dict format
|
|
37
|
+
"TextAnalyzeDictFormat",
|
|
38
|
+
# Support
|
|
39
|
+
"DictAnalyzeResult",
|
|
37
40
|
]
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
# Library
|
|
41
44
|
###########################################################################
|
|
42
|
-
from collections import Counter
|
|
43
|
-
from itertools import accumulate, chain, groupby
|
|
44
45
|
import math
|
|
45
46
|
import operator
|
|
46
47
|
import random
|
|
47
|
-
from
|
|
48
|
-
from
|
|
49
|
-
|
|
50
|
-
from
|
|
51
|
-
|
|
48
|
+
from collections import Counter
|
|
49
|
+
from itertools import accumulate, chain, groupby
|
|
50
|
+
from sys import version_info as _python_version
|
|
51
|
+
from typing import (
|
|
52
|
+
Any,
|
|
53
|
+
Callable,
|
|
54
|
+
Dict,
|
|
55
|
+
List,
|
|
56
|
+
NamedTuple,
|
|
57
|
+
Optional,
|
|
58
|
+
Tuple,
|
|
59
|
+
TypedDict,
|
|
60
|
+
Union,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if _python_version.minor >= 11:
|
|
64
|
+
from typing import NotRequired
|
|
65
|
+
else:
|
|
66
|
+
from typing_extensions import NotRequired # type: ignore
|
|
67
|
+
|
|
68
|
+
from deprecated.sphinx import versionadded, versionchanged
|
|
69
|
+
|
|
70
|
+
from absfuyu.general.generator import Charset, Generator
|
|
71
|
+
from absfuyu.logger import _compress_list_for_print, logger
|
|
52
72
|
from absfuyu.util import set_min, set_min_max
|
|
53
73
|
|
|
54
74
|
|
|
@@ -68,7 +88,7 @@ def _dict_bool(dict_object: dict, option: bool) -> Optional[dict]:
|
|
|
68
88
|
return None
|
|
69
89
|
|
|
70
90
|
|
|
71
|
-
# Sub class
|
|
91
|
+
# MARK: Sub class
|
|
72
92
|
###########################################################################
|
|
73
93
|
class Pow:
|
|
74
94
|
"""Number power by a number"""
|
|
@@ -93,7 +113,7 @@ class Pow:
|
|
|
93
113
|
|
|
94
114
|
:rtype: list[int | float]
|
|
95
115
|
"""
|
|
96
|
-
return [self.number] * self.power_by
|
|
116
|
+
return [self.number] * int(self.power_by)
|
|
97
117
|
|
|
98
118
|
def calculate(self) -> float:
|
|
99
119
|
"""
|
|
@@ -148,14 +168,59 @@ class DictBoolFalse(Dict[Any, bool]):
|
|
|
148
168
|
# return out.__repr__()
|
|
149
169
|
|
|
150
170
|
|
|
171
|
+
class TextAnalyzeDictFormat(TypedDict):
|
|
172
|
+
"""
|
|
173
|
+
Dict format for ``Text.analyze()`` method
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
digit : int
|
|
178
|
+
Number of digit characters
|
|
179
|
+
|
|
180
|
+
uppercase : int
|
|
181
|
+
Number of uppercase characters
|
|
182
|
+
|
|
183
|
+
lowercase : int
|
|
184
|
+
Number of lowercase characters
|
|
185
|
+
|
|
186
|
+
other : int
|
|
187
|
+
Number of other printable characters
|
|
188
|
+
|
|
189
|
+
is_pangram : NotRequired[bool]
|
|
190
|
+
Is a pangram (Not required)
|
|
191
|
+
|
|
192
|
+
is_palindrome : NotRequired[bool]
|
|
193
|
+
Is a palindrome (Not required)
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
digit: int
|
|
197
|
+
uppercase: int
|
|
198
|
+
lowercase: int
|
|
199
|
+
other: int
|
|
200
|
+
is_pangram: NotRequired[bool]
|
|
201
|
+
is_palindrome: NotRequired[bool]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class DictAnalyzeResult(NamedTuple):
|
|
205
|
+
"""
|
|
206
|
+
Result for ``DictExt.analyze()``
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
max_value: Union[int, float]
|
|
210
|
+
min_value: Union[int, float]
|
|
211
|
+
max_list: list
|
|
212
|
+
min_list: list
|
|
213
|
+
|
|
214
|
+
|
|
151
215
|
# Class
|
|
152
216
|
###########################################################################
|
|
217
|
+
# MARK: Text
|
|
153
218
|
class Text(str):
|
|
154
219
|
"""
|
|
155
220
|
``str`` extension
|
|
156
221
|
"""
|
|
157
222
|
|
|
158
|
-
def divide(self, string_split_size: int = 60) ->
|
|
223
|
+
def divide(self, string_split_size: int = 60) -> List[str]:
|
|
159
224
|
"""
|
|
160
225
|
Divide long string into smaller size
|
|
161
226
|
|
|
@@ -167,7 +232,7 @@ class Text(str):
|
|
|
167
232
|
|
|
168
233
|
Returns
|
|
169
234
|
-------
|
|
170
|
-
list
|
|
235
|
+
list[str]
|
|
171
236
|
A list in which each item is a smaller
|
|
172
237
|
string with the size of ``string_split_size``
|
|
173
238
|
(need to be concaternate later)
|
|
@@ -179,7 +244,7 @@ class Text(str):
|
|
|
179
244
|
>>> test.divide(string_split_size=20)
|
|
180
245
|
['This is an extremely', ' long line of text!']
|
|
181
246
|
"""
|
|
182
|
-
temp = self
|
|
247
|
+
temp = str(self)
|
|
183
248
|
output = []
|
|
184
249
|
while len(temp) != 0:
|
|
185
250
|
output.append(temp[:string_split_size])
|
|
@@ -243,7 +308,6 @@ class Text(str):
|
|
|
243
308
|
output = []
|
|
244
309
|
|
|
245
310
|
# split variable
|
|
246
|
-
splt_var_len = split_var_len
|
|
247
311
|
splt_len = len(temp)
|
|
248
312
|
|
|
249
313
|
if custom_var_name is None:
|
|
@@ -283,13 +347,20 @@ class Text(str):
|
|
|
283
347
|
|
|
284
348
|
return output
|
|
285
349
|
|
|
286
|
-
|
|
350
|
+
@versionchanged(version="3.3.0", reason="Update functionality")
|
|
351
|
+
def analyze(self, full: bool = False) -> TextAnalyzeDictFormat:
|
|
287
352
|
"""
|
|
288
353
|
String analyze (count number of type of character)
|
|
289
354
|
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
full : bool
|
|
358
|
+
Full analyze when ``True``
|
|
359
|
+
(Default: ``False``)
|
|
360
|
+
|
|
290
361
|
Returns
|
|
291
362
|
-------
|
|
292
|
-
dict
|
|
363
|
+
dict | TextAnalyzeDictFormat
|
|
293
364
|
A dictionary contains number of digit character,
|
|
294
365
|
uppercase character, lowercase character, and
|
|
295
366
|
special character
|
|
@@ -304,7 +375,12 @@ class Text(str):
|
|
|
304
375
|
|
|
305
376
|
temp = self
|
|
306
377
|
|
|
307
|
-
detail = {
|
|
378
|
+
detail: TextAnalyzeDictFormat = {
|
|
379
|
+
"digit": 0,
|
|
380
|
+
"uppercase": 0,
|
|
381
|
+
"lowercase": 0,
|
|
382
|
+
"other": 0,
|
|
383
|
+
}
|
|
308
384
|
|
|
309
385
|
for x in temp:
|
|
310
386
|
if ord(x) in range(48, 58): # num
|
|
@@ -316,6 +392,10 @@ class Text(str):
|
|
|
316
392
|
else:
|
|
317
393
|
detail["other"] += 1
|
|
318
394
|
|
|
395
|
+
if full:
|
|
396
|
+
detail["is_palindrome"] = self.is_palindrome()
|
|
397
|
+
detail["is_pangram"] = self.is_pangram()
|
|
398
|
+
|
|
319
399
|
return detail
|
|
320
400
|
|
|
321
401
|
def reverse(self) -> "Text":
|
|
@@ -334,7 +414,7 @@ class Text(str):
|
|
|
334
414
|
>>> test.reverse()
|
|
335
415
|
'!dlroW ,olleH'
|
|
336
416
|
"""
|
|
337
|
-
return __class__(self[::-1])
|
|
417
|
+
return self.__class__(self[::-1])
|
|
338
418
|
|
|
339
419
|
def is_pangram(self) -> bool:
|
|
340
420
|
"""
|
|
@@ -403,7 +483,7 @@ class Text(str):
|
|
|
403
483
|
|
|
404
484
|
for i in range(str_len):
|
|
405
485
|
if i % 2 == 0:
|
|
406
|
-
temp.append(
|
|
486
|
+
temp.append("\\x")
|
|
407
487
|
temp.append(hex_str[i])
|
|
408
488
|
return "".join(temp)
|
|
409
489
|
else:
|
|
@@ -431,7 +511,7 @@ class Text(str):
|
|
|
431
511
|
>>> test.random_capslock()
|
|
432
512
|
'tHis iS An ExtREmELY loNg liNE oF tExT!'
|
|
433
513
|
"""
|
|
434
|
-
probability = set_min_max(probability)
|
|
514
|
+
probability = int(set_min_max(probability))
|
|
435
515
|
text = self.lower()
|
|
436
516
|
|
|
437
517
|
temp = []
|
|
@@ -440,7 +520,7 @@ class Text(str):
|
|
|
440
520
|
x = x.upper()
|
|
441
521
|
temp.append(x)
|
|
442
522
|
logger.debug(temp)
|
|
443
|
-
return __class__("".join(temp))
|
|
523
|
+
return self.__class__("".join(temp))
|
|
444
524
|
|
|
445
525
|
def reverse_capslock(self) -> "Text":
|
|
446
526
|
"""
|
|
@@ -464,12 +544,12 @@ class Text(str):
|
|
|
464
544
|
temp[i] = x.lower()
|
|
465
545
|
else:
|
|
466
546
|
temp[i] = x.upper()
|
|
467
|
-
return __class__("".join(temp))
|
|
547
|
+
return self.__class__("".join(temp))
|
|
468
548
|
|
|
469
549
|
def to_list(self) -> List[str]:
|
|
470
550
|
"""
|
|
471
551
|
Convert into list
|
|
472
|
-
|
|
552
|
+
|
|
473
553
|
Returns
|
|
474
554
|
-------
|
|
475
555
|
list[str]
|
|
@@ -484,7 +564,111 @@ class Text(str):
|
|
|
484
564
|
"""
|
|
485
565
|
return list(self)
|
|
486
566
|
|
|
567
|
+
@versionadded(version="3.3.0")
|
|
568
|
+
def to_listext(self) -> "ListExt":
|
|
569
|
+
"""
|
|
570
|
+
Convert into ``ListExt``
|
|
571
|
+
|
|
572
|
+
Returns
|
|
573
|
+
-------
|
|
574
|
+
ListExt[str]
|
|
575
|
+
List of string
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
Example:
|
|
579
|
+
--------
|
|
580
|
+
>>> test = Text("test")
|
|
581
|
+
>>> test.to_listext()
|
|
582
|
+
['t', 'e', 's', 't']
|
|
583
|
+
"""
|
|
584
|
+
return ListExt(self)
|
|
585
|
+
|
|
586
|
+
@versionadded(version="3.3.0")
|
|
587
|
+
def count_pattern(self, pattern: str, ignore_capslock: bool = False) -> int:
|
|
588
|
+
"""
|
|
589
|
+
Returns how many times ``pattern`` appears in text
|
|
590
|
+
(Inspired by hackerrank exercise)
|
|
487
591
|
|
|
592
|
+
Parameters
|
|
593
|
+
----------
|
|
594
|
+
pattern : str
|
|
595
|
+
Pattern to count
|
|
596
|
+
|
|
597
|
+
ignore_capslock : bool
|
|
598
|
+
Ignore the pattern uppercase or lowercase
|
|
599
|
+
(Default: ``False`` - Exact match)
|
|
600
|
+
|
|
601
|
+
Returns
|
|
602
|
+
-------
|
|
603
|
+
int
|
|
604
|
+
How many times pattern appeared
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
Example:
|
|
608
|
+
--------
|
|
609
|
+
>>> Text("test").count_pattern("t")
|
|
610
|
+
2
|
|
611
|
+
"""
|
|
612
|
+
if len(pattern) > len(self):
|
|
613
|
+
raise ValueError(f"len(pattern) must not larger than {len(self)}")
|
|
614
|
+
|
|
615
|
+
temp = str(self)
|
|
616
|
+
if ignore_capslock:
|
|
617
|
+
pattern = pattern.lower()
|
|
618
|
+
temp = temp.lower()
|
|
619
|
+
|
|
620
|
+
out = [
|
|
621
|
+
1
|
|
622
|
+
for i in range(len(temp) - len(pattern) + 1)
|
|
623
|
+
if temp[i : i + len(pattern)] == pattern
|
|
624
|
+
]
|
|
625
|
+
return sum(out)
|
|
626
|
+
|
|
627
|
+
@versionadded(version="3.3.0")
|
|
628
|
+
def hapax(self, strict: bool = False) -> List[str]:
|
|
629
|
+
"""
|
|
630
|
+
A hapax legomenon (often abbreviated to hapax)
|
|
631
|
+
is a word which occurs only once in either
|
|
632
|
+
the written record of a language, the works of
|
|
633
|
+
an author, or in a single text.
|
|
634
|
+
|
|
635
|
+
This function returns a list of hapaxes (if any)
|
|
636
|
+
(Lettercase is ignored)
|
|
637
|
+
|
|
638
|
+
Parameters
|
|
639
|
+
----------
|
|
640
|
+
strict : bool
|
|
641
|
+
Remove all special characters before checking for hapax
|
|
642
|
+
(Default: ``False``)
|
|
643
|
+
|
|
644
|
+
Returns
|
|
645
|
+
-------
|
|
646
|
+
list[str]
|
|
647
|
+
A list of hapaxes
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
Example:
|
|
651
|
+
--------
|
|
652
|
+
>>> test = Text("A a. a, b c c= C| d d")
|
|
653
|
+
>>> test.hapax()
|
|
654
|
+
['a', 'a.', 'a,', 'b', 'c', 'c=', 'c|']
|
|
655
|
+
|
|
656
|
+
>>> test.hapax(strict=True)
|
|
657
|
+
['b']
|
|
658
|
+
"""
|
|
659
|
+
word_list: List[str] = self.lower().split()
|
|
660
|
+
if strict:
|
|
661
|
+
remove_characters: List[str] = list(r"\"'.,:;|()[]{}\/!@#$%^&*-_=+?<>`~")
|
|
662
|
+
temp = str(self)
|
|
663
|
+
for x in remove_characters:
|
|
664
|
+
temp = temp.replace(x, "")
|
|
665
|
+
word_list = temp.lower().split()
|
|
666
|
+
|
|
667
|
+
hapaxes = filter(lambda x: word_list.count(x) == 1, word_list)
|
|
668
|
+
return list(hapaxes)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
# MARK: IntNumber
|
|
488
672
|
class IntNumber(int):
|
|
489
673
|
"""
|
|
490
674
|
``int`` extension
|
|
@@ -640,7 +824,7 @@ class IntNumber(int):
|
|
|
640
824
|
# a perfect number have a form of (2**(n-1))*((2**n)-1)
|
|
641
825
|
perfect_number.append((2**(x-1))*((2**x)-1))
|
|
642
826
|
"""
|
|
643
|
-
number = self
|
|
827
|
+
number = int(self)
|
|
644
828
|
|
|
645
829
|
perfect_number = [
|
|
646
830
|
6,
|
|
@@ -653,15 +837,15 @@ class IntNumber(int):
|
|
|
653
837
|
2_305_843_008_139_952_128,
|
|
654
838
|
]
|
|
655
839
|
|
|
656
|
-
if
|
|
840
|
+
if number in perfect_number:
|
|
657
841
|
return True
|
|
658
842
|
|
|
659
|
-
elif
|
|
843
|
+
elif number < perfect_number[-1]:
|
|
660
844
|
return False
|
|
661
845
|
|
|
662
846
|
else:
|
|
663
847
|
# Faster way to check
|
|
664
|
-
perfect_number_index = [
|
|
848
|
+
perfect_number_index: List[int] = [
|
|
665
849
|
61,
|
|
666
850
|
89,
|
|
667
851
|
107,
|
|
@@ -709,9 +893,9 @@ class IntNumber(int):
|
|
|
709
893
|
for x in perfect_number_index:
|
|
710
894
|
# a perfect number have a form of (2**(n-1))*((2**n)-1)
|
|
711
895
|
perfect_number = (2 ** (x - 1)) * ((2**x) - 1)
|
|
712
|
-
if number < perfect_number:
|
|
896
|
+
if number < perfect_number: # type: ignore
|
|
713
897
|
return False
|
|
714
|
-
elif number == perfect_number:
|
|
898
|
+
elif number == perfect_number: # type: ignore
|
|
715
899
|
return True
|
|
716
900
|
|
|
717
901
|
# Manual way when above method not working
|
|
@@ -721,7 +905,7 @@ class IntNumber(int):
|
|
|
721
905
|
i = 2
|
|
722
906
|
while i * i <= number:
|
|
723
907
|
if number % i == 0:
|
|
724
|
-
s += +i + number / i
|
|
908
|
+
s += +i + number / i # type: ignore
|
|
725
909
|
i += 1
|
|
726
910
|
# s == number -> perfect
|
|
727
911
|
return True if s == number and number != 1 else False
|
|
@@ -745,8 +929,9 @@ class IntNumber(int):
|
|
|
745
929
|
"""
|
|
746
930
|
try:
|
|
747
931
|
check = sum([int(x) ** len(str(self)) for x in str(self)])
|
|
748
|
-
|
|
749
|
-
|
|
932
|
+
res = int(self) == check
|
|
933
|
+
return res # type: ignore
|
|
934
|
+
except Exception:
|
|
750
935
|
return False
|
|
751
936
|
|
|
752
937
|
def reverse(self) -> "IntNumber":
|
|
@@ -765,10 +950,10 @@ class IntNumber(int):
|
|
|
765
950
|
>>> test.reverse()
|
|
766
951
|
201
|
|
767
952
|
"""
|
|
768
|
-
number = self
|
|
953
|
+
number = int(self)
|
|
769
954
|
if number <= 1:
|
|
770
955
|
number *= -1
|
|
771
|
-
return __class__(str(number)[::-1])
|
|
956
|
+
return self.__class__(str(number)[::-1])
|
|
772
957
|
|
|
773
958
|
def is_palindromic(self) -> bool:
|
|
774
959
|
"""
|
|
@@ -797,6 +982,7 @@ class IntNumber(int):
|
|
|
797
982
|
return self.is_palindromic() and self.is_prime()
|
|
798
983
|
|
|
799
984
|
# calculation stuff
|
|
985
|
+
@versionchanged(version="3.3.0", reason="Fix bug")
|
|
800
986
|
def lcm(self, with_number: int) -> "IntNumber":
|
|
801
987
|
"""
|
|
802
988
|
Least common multiple of ``self`` and ``with_number``
|
|
@@ -818,8 +1004,12 @@ class IntNumber(int):
|
|
|
818
1004
|
>>> test.lcm(5)
|
|
819
1005
|
510
|
|
820
1006
|
"""
|
|
821
|
-
|
|
1007
|
+
try:
|
|
1008
|
+
return self.__class__(math.lcm(self, with_number))
|
|
1009
|
+
except AttributeError: # Python < 3.9
|
|
1010
|
+
return self.__class__((self * with_number) // math.gcd(self, with_number))
|
|
822
1011
|
|
|
1012
|
+
@versionchanged(version="3.3.0", reason="Fix bug")
|
|
823
1013
|
def gcd(self, with_number: int) -> "IntNumber":
|
|
824
1014
|
"""
|
|
825
1015
|
Greatest common divisor of ``self`` and ``with_number``
|
|
@@ -841,10 +1031,7 @@ class IntNumber(int):
|
|
|
841
1031
|
>>> test.gcd(8)
|
|
842
1032
|
8
|
|
843
1033
|
"""
|
|
844
|
-
|
|
845
|
-
return __class__(math.gcd(self, with_number))
|
|
846
|
-
else:
|
|
847
|
-
return __class__(math.lcm(self, with_number))
|
|
1034
|
+
return self.__class__(math.gcd(self, with_number))
|
|
848
1035
|
|
|
849
1036
|
def add_to_one_digit(self, master_number: bool = False) -> "IntNumber":
|
|
850
1037
|
"""
|
|
@@ -873,7 +1060,7 @@ class IntNumber(int):
|
|
|
873
1060
|
>>> test.add_to_one_digit(master_number=True)
|
|
874
1061
|
11
|
|
875
1062
|
"""
|
|
876
|
-
number = self
|
|
1063
|
+
number = int(self)
|
|
877
1064
|
if number < 0:
|
|
878
1065
|
number *= -1
|
|
879
1066
|
logger.debug(f"Current number: {number}")
|
|
@@ -883,7 +1070,7 @@ class IntNumber(int):
|
|
|
883
1070
|
if number == 22 or number == 11:
|
|
884
1071
|
break # Master number
|
|
885
1072
|
logger.debug(f"Sum after loop: {number}")
|
|
886
|
-
return __class__(number)
|
|
1073
|
+
return self.__class__(number)
|
|
887
1074
|
|
|
888
1075
|
def divisible_list(self, short_form: bool = True) -> List[int]:
|
|
889
1076
|
"""
|
|
@@ -1024,9 +1211,10 @@ class IntNumber(int):
|
|
|
1024
1211
|
characteristic = DictBoolTrue(characteristic)
|
|
1025
1212
|
|
|
1026
1213
|
output["characteristic"] = characteristic
|
|
1027
|
-
return output
|
|
1214
|
+
return output # type: ignore
|
|
1028
1215
|
|
|
1029
1216
|
|
|
1217
|
+
# MARK: ListExt
|
|
1030
1218
|
class ListExt(list):
|
|
1031
1219
|
"""
|
|
1032
1220
|
``list`` extension
|
|
@@ -1065,7 +1253,9 @@ class ListExt(list):
|
|
|
1065
1253
|
list
|
|
1066
1254
|
Filtered list
|
|
1067
1255
|
"""
|
|
1068
|
-
number_of_items =
|
|
1256
|
+
number_of_items = int(
|
|
1257
|
+
set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1258
|
+
)
|
|
1069
1259
|
return self[:number_of_items]
|
|
1070
1260
|
|
|
1071
1261
|
def tail(self, number_of_items: int = 5) -> list:
|
|
@@ -1083,7 +1273,9 @@ class ListExt(list):
|
|
|
1083
1273
|
list
|
|
1084
1274
|
Filtered list
|
|
1085
1275
|
"""
|
|
1086
|
-
number_of_items =
|
|
1276
|
+
number_of_items = int(
|
|
1277
|
+
set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1278
|
+
)
|
|
1087
1279
|
return self[::-1][:number_of_items][::-1]
|
|
1088
1280
|
|
|
1089
1281
|
def sorts(self, reverse: bool = False) -> "ListExt":
|
|
@@ -1110,7 +1302,7 @@ class ListExt(list):
|
|
|
1110
1302
|
[1, 9, 'aaa', 'abc', 1.4, 3.5]
|
|
1111
1303
|
"""
|
|
1112
1304
|
lst = self.copy()
|
|
1113
|
-
type_weights = {}
|
|
1305
|
+
type_weights: dict = {}
|
|
1114
1306
|
for x in lst:
|
|
1115
1307
|
if type(x) not in type_weights:
|
|
1116
1308
|
type_weights[type(x)] = len(type_weights)
|
|
@@ -1121,14 +1313,14 @@ class ListExt(list):
|
|
|
1121
1313
|
)
|
|
1122
1314
|
|
|
1123
1315
|
logger.debug(output)
|
|
1124
|
-
return __class__(output)
|
|
1316
|
+
return self.__class__(output)
|
|
1125
1317
|
|
|
1126
1318
|
def freq(
|
|
1127
1319
|
self,
|
|
1128
1320
|
sort: bool = False,
|
|
1129
1321
|
num_of_first_char: Optional[int] = None,
|
|
1130
1322
|
appear_increment: bool = False,
|
|
1131
|
-
):
|
|
1323
|
+
) -> Union[dict, List[int]]:
|
|
1132
1324
|
"""
|
|
1133
1325
|
Find frequency of each item in list
|
|
1134
1326
|
|
|
@@ -1140,7 +1332,7 @@ class ListExt(list):
|
|
|
1140
1332
|
|
|
1141
1333
|
num_of_first_char : int | None
|
|
1142
1334
|
| Number of first character taken into account to sort
|
|
1143
|
-
| (Default: None)
|
|
1335
|
+
| (Default: ``None``)
|
|
1144
1336
|
| (num_of_first_char = ``1``: first character in each item)
|
|
1145
1337
|
|
|
1146
1338
|
appear_increment : bool
|
|
@@ -1152,7 +1344,7 @@ class ListExt(list):
|
|
|
1152
1344
|
dict
|
|
1153
1345
|
A dict that show frequency
|
|
1154
1346
|
|
|
1155
|
-
list
|
|
1347
|
+
list[int]
|
|
1156
1348
|
Incremental index list
|
|
1157
1349
|
|
|
1158
1350
|
|
|
@@ -1168,14 +1360,14 @@ class ListExt(list):
|
|
|
1168
1360
|
"""
|
|
1169
1361
|
|
|
1170
1362
|
if sort:
|
|
1171
|
-
data = self.sorts()
|
|
1363
|
+
data = self.sorts().copy()
|
|
1172
1364
|
else:
|
|
1173
1365
|
data = self.copy()
|
|
1174
1366
|
|
|
1175
1367
|
if num_of_first_char is None:
|
|
1176
1368
|
temp = Counter(data)
|
|
1177
1369
|
else:
|
|
1178
|
-
max_char = min([len(str(x)) for x in data])
|
|
1370
|
+
max_char: int = min([len(str(x)) for x in data])
|
|
1179
1371
|
logger.debug(f"Max character: {max_char}")
|
|
1180
1372
|
if num_of_first_char not in range(1, max_char):
|
|
1181
1373
|
logger.debug(f"Not in {range(1, max_char)}. Using default value...")
|
|
@@ -1186,12 +1378,12 @@ class ListExt(list):
|
|
|
1186
1378
|
|
|
1187
1379
|
try:
|
|
1188
1380
|
times_appear = dict(sorted(temp.items()))
|
|
1189
|
-
except:
|
|
1190
|
-
times_appear = dict(__class__(temp.items()).sorts())
|
|
1381
|
+
except Exception:
|
|
1382
|
+
times_appear = dict(self.__class__(temp.items()).sorts())
|
|
1191
1383
|
logger.debug(times_appear)
|
|
1192
1384
|
|
|
1193
1385
|
if appear_increment:
|
|
1194
|
-
times_appear_increment = list(
|
|
1386
|
+
times_appear_increment: List[int] = list(
|
|
1195
1387
|
accumulate(times_appear.values(), operator.add)
|
|
1196
1388
|
)
|
|
1197
1389
|
logger.debug(times_appear_increment)
|
|
@@ -1329,7 +1521,7 @@ class ListExt(list):
|
|
|
1329
1521
|
['1', '2', '3']
|
|
1330
1522
|
"""
|
|
1331
1523
|
# return __class__(func(x) for x in self)
|
|
1332
|
-
return __class__(map(func, self))
|
|
1524
|
+
return self.__class__(map(func, self))
|
|
1333
1525
|
|
|
1334
1526
|
def unique(self) -> "ListExt":
|
|
1335
1527
|
"""
|
|
@@ -1347,7 +1539,7 @@ class ListExt(list):
|
|
|
1347
1539
|
>>> test.unique()
|
|
1348
1540
|
[1, 2, 3]
|
|
1349
1541
|
"""
|
|
1350
|
-
return __class__(set(self))
|
|
1542
|
+
return self.__class__(set(self))
|
|
1351
1543
|
|
|
1352
1544
|
def group_by_unique(self) -> "ListExt":
|
|
1353
1545
|
"""
|
|
@@ -1371,7 +1563,7 @@ class ListExt(list):
|
|
|
1371
1563
|
|
|
1372
1564
|
# New
|
|
1373
1565
|
temp = groupby(self.sorts())
|
|
1374
|
-
return __class__([list(g) for _, g in temp])
|
|
1566
|
+
return self.__class__([list(g) for _, g in temp])
|
|
1375
1567
|
|
|
1376
1568
|
@staticmethod
|
|
1377
1569
|
def _group_by_unique(iterable: list) -> List[list]:
|
|
@@ -1414,7 +1606,7 @@ class ListExt(list):
|
|
|
1414
1606
|
iter = self.copy()
|
|
1415
1607
|
|
|
1416
1608
|
# Init loop
|
|
1417
|
-
for _ in range(set_min(max_loop, min_value=3)):
|
|
1609
|
+
for _ in range(int(set_min(max_loop, min_value=3))):
|
|
1418
1610
|
temp: Dict[Any, list] = {}
|
|
1419
1611
|
# Make dict{key: all `item` that contains `key`}
|
|
1420
1612
|
for item in iter:
|
|
@@ -1450,7 +1642,7 @@ class ListExt(list):
|
|
|
1450
1642
|
"""
|
|
1451
1643
|
try:
|
|
1452
1644
|
return ListExt(chain(*self))
|
|
1453
|
-
except:
|
|
1645
|
+
except Exception:
|
|
1454
1646
|
temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
|
|
1455
1647
|
return ListExt(chain(*temp))
|
|
1456
1648
|
|
|
@@ -1477,7 +1669,7 @@ class ListExt(list):
|
|
|
1477
1669
|
>>> test.numbering()
|
|
1478
1670
|
[(0, 9), (1, 9), (2, 9)]
|
|
1479
1671
|
"""
|
|
1480
|
-
start = set_min(start, min_value=0)
|
|
1672
|
+
start = int(set_min(start, min_value=0))
|
|
1481
1673
|
return ListExt(enumerate(self, start=start))
|
|
1482
1674
|
|
|
1483
1675
|
@staticmethod
|
|
@@ -1485,16 +1677,21 @@ class ListExt(list):
|
|
|
1485
1677
|
"""
|
|
1486
1678
|
Static method for ``numbering``
|
|
1487
1679
|
"""
|
|
1488
|
-
start = set_min(start, min_value=0)
|
|
1680
|
+
start = int(set_min(start, min_value=0))
|
|
1489
1681
|
return list(enumerate(iterable, start=start))
|
|
1490
1682
|
|
|
1491
1683
|
|
|
1684
|
+
# MARK: DictExt
|
|
1492
1685
|
class DictExt(dict):
|
|
1493
1686
|
"""
|
|
1494
1687
|
``dict`` extension
|
|
1495
1688
|
"""
|
|
1496
1689
|
|
|
1497
|
-
|
|
1690
|
+
@versionchanged(
|
|
1691
|
+
version="3.3.0",
|
|
1692
|
+
reason="Change function output; Before: <dict>, Now: DictAnalyzeResult",
|
|
1693
|
+
)
|
|
1694
|
+
def analyze(self) -> DictAnalyzeResult:
|
|
1498
1695
|
"""
|
|
1499
1696
|
Analyze all the key values (``int``, ``float``)
|
|
1500
1697
|
in ``dict`` then return highest/lowest index
|
|
@@ -1515,18 +1712,13 @@ class DictExt(dict):
|
|
|
1515
1712
|
>>> "mno": 1
|
|
1516
1713
|
>>> })
|
|
1517
1714
|
>>> test.analyze()
|
|
1518
|
-
|
|
1519
|
-
'max_value': 9,
|
|
1520
|
-
'min_value': 1,
|
|
1521
|
-
'max': [('abc', 9), ('def', 9)],
|
|
1522
|
-
'min': [('jkl', 1), ('mno', 1)]
|
|
1523
|
-
}
|
|
1715
|
+
DictAnalyzeResult(max_value=9, min_value=1, max_list=[('abc', 9), ('def', 9)], min_list=[('jkl', 1), ('mno', 1)])
|
|
1524
1716
|
"""
|
|
1525
1717
|
try:
|
|
1526
|
-
dct = self.copy()
|
|
1718
|
+
dct: dict = self.copy()
|
|
1527
1719
|
|
|
1528
|
-
max_val = max(list(dct.values()))
|
|
1529
|
-
min_val = min(list(dct.values()))
|
|
1720
|
+
max_val: Union[int, float] = max(list(dct.values()))
|
|
1721
|
+
min_val: Union[int, float] = min(list(dct.values()))
|
|
1530
1722
|
max_list = []
|
|
1531
1723
|
min_list = []
|
|
1532
1724
|
|
|
@@ -1536,20 +1728,21 @@ class DictExt(dict):
|
|
|
1536
1728
|
if v == min_val:
|
|
1537
1729
|
min_list.append((k, v))
|
|
1538
1730
|
|
|
1539
|
-
output = {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
}
|
|
1731
|
+
# output = {
|
|
1732
|
+
# "max_value": max_val,
|
|
1733
|
+
# "min_value": min_val,
|
|
1734
|
+
# "max": max_list,
|
|
1735
|
+
# "min": min_list,
|
|
1736
|
+
# }
|
|
1545
1737
|
|
|
1546
|
-
logger.debug(output)
|
|
1547
|
-
return output
|
|
1738
|
+
# logger.debug(output)
|
|
1739
|
+
# return output
|
|
1740
|
+
return DictAnalyzeResult(max_val, min_val, max_list, min_list)
|
|
1548
1741
|
|
|
1549
|
-
except:
|
|
1742
|
+
except Exception:
|
|
1550
1743
|
err_msg = "Value must be int or float"
|
|
1551
1744
|
logger.error(err_msg)
|
|
1552
|
-
raise ValueError(err_msg)
|
|
1745
|
+
raise ValueError(err_msg) # noqa: B904
|
|
1553
1746
|
|
|
1554
1747
|
def swap_items(self) -> "DictExt":
|
|
1555
1748
|
"""
|
|
@@ -1567,7 +1760,7 @@ class DictExt(dict):
|
|
|
1567
1760
|
>>> test.swap_items()
|
|
1568
1761
|
{9: 'abc'}
|
|
1569
1762
|
"""
|
|
1570
|
-
return __class__(zip(self.values(), self.keys()))
|
|
1763
|
+
return self.__class__(zip(self.values(), self.keys()))
|
|
1571
1764
|
|
|
1572
1765
|
def apply(self, func: Callable, apply_to_value: bool = True) -> "DictExt":
|
|
1573
1766
|
"""
|
|
@@ -1598,13 +1791,12 @@ class DictExt(dict):
|
|
|
1598
1791
|
k = self.keys()
|
|
1599
1792
|
v = map(func, self.values())
|
|
1600
1793
|
else:
|
|
1601
|
-
k = map(func, self.keys())
|
|
1602
|
-
v = self.values()
|
|
1603
|
-
return __class__(zip(k, v))
|
|
1794
|
+
k = map(func, self.keys()) # type: ignore
|
|
1795
|
+
v = self.values() # type: ignore
|
|
1796
|
+
return self.__class__(zip(k, v))
|
|
1604
1797
|
|
|
1605
1798
|
|
|
1606
1799
|
# Run
|
|
1607
1800
|
###########################################################################
|
|
1608
1801
|
if __name__ == "__main__":
|
|
1609
1802
|
logger.setLevel(10)
|
|
1610
|
-
# from rich import print
|