-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.py
235 lines (182 loc) · 6.54 KB
/
util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
'''实用工具函数
- 🎯内容分离:减少在「工具链」侧的代码量,拆分功能
'''
from time import strftime, localtime
from typing import Any, Callable, Iterable, Iterator, List, Optional, TypeVar
UTF_8_SIG = b'\xEF\xBB\xBF'
'''UTF8 BOM:明确标识一个文件是UTF-8编码
- 🔗参考:https://baike.baidu.com/item/BOM/2790401
'''
def show(thing):
'''打印值,并返回该值'''
print(thing)
return thing
__T = TypeVar("__T")
'''泛型参数
- 📝Python 3.1.2才在语言层面增加泛型语法
- 🔗参考:https://docs.python.org/zh-cn/3/library/typing.html
- 🚩【2024-05-27 11:34:51】不直接使用`T`以避免符号泄漏
'''
def collect(iterable: Iterable[__T]) -> List[__T]:
'''将可迭代对象转换为列表
- 🚩直接使用列表生成式
'''
return [t for t in iterable]
def __f_range(start: float, stop: float, step: float) -> Iterable[float]:
'''浮点范围
- 🚩类似Python内置的`range`函数,但能使用浮点
- 🔗参考:https://stackoverflow.com/questions/7267226/range-for-floats
- ⚠️只能用一次:迭代/遍历 完成就失效,不能被用作「常持有变量」
'''
result, n = start, 1
while result <= stop:
yield result
result = start + n * step
n += 1
def f_range(start: float, stop: float, step: float = 0.1) -> List[float]:
'''浮点范围(固定数组)
- 📌将Python内置的列表生成式进行了包装
- 🚩每次运行均生成一个新数组,这个数组可被重复遍历
'''
return collect(__f_range(start, stop, step))
def time_stamp():
'''产生视觉上连续的数值字符串作为时间戳
- 🚩格式:年月日时分秒(按【调用时】时间算)
- 📄"20240526191722" ⇒ 2024年05月26日 19:17:22
'''
return strftime('%Y%m%d%H%M%S', localtime())
def count(iter: Iterable) -> int:
'''计算可迭代对象的元素数量
- 🚩直接使用内置函数
'''
count = 0
for _ in iter:
count += 1
return count
def is_empty(iter: Iterable) -> bool:
'''判断一个可迭代对象是否为空
- 🚩直接使用`count`计算
- ⚠️会消耗掉迭代器
'''
return count(iter) == 0
def first(iterable: Iterable[__T]) -> Optional[__T]:
'''获取可迭代对象的第一个元素(可能没有)'''
return next(iter(iterable))
def is_same(iterable: Iterable) -> bool:
'''判断一个可迭代对象内含元素是否相同
- 🚩空迭代器⇒真
'''
iterator = iter(iterable)
try: # ! ❌is_empty会消耗掉迭代器
first = next(iterator)
except StopIteration:
return True # 空⇒真
# 判断剩余元素剩余元素
return all(t == first for t in iterator)
def not_same(iterable: Iterable) -> bool:
'''判断一个可迭代对象内含元素是否不同
- 🚩空迭代器⇒假
- 📌实质上就是`is_same`的反向
'''
iterator = iter(iterable)
try: # ! ❌is_empty会消耗掉迭代器
first = next(iterator)
except StopIteration:
return False # 空⇒假
# 判断剩余元素剩余元素
return any(t != first for t in iterator)
def find_first(iterable: Iterable[__T], predicate: Callable[[__T], bool]) -> Optional[__T]:
'''查找第一个满足条件的元素
- 🚩直接遍历判断
'''
for t in iterable:
if predicate(t):
return t
return None
char = str
'''字符类型'''
def is_full_scale_char(c: char) -> bool:
'''判断一个字符是否为全角字符
- 🚩【2024-05-27 11:16:07】目前仅用于判断是否中文
'''
return 0x4e00 < ord(c) < 0x9fff
def num_full_scale_chars(s: str) -> int:
'''计算一个字符串中全角字符的数量
- 🎯【2024-05-27 11:13:15】用于「最大长度补全全角空格」
'''
return count(filter(is_full_scale_char, s))
def pad_full_scale_spaces(s: str, max_num_full_scale_chars: int) -> str:
'''补全字符串,使其全角字符个数恰好为max_num_full_scale_chars
- 🎯此即「最大长度补全全角空格」,避免Python默认补全导致宽度问题
- 🚩对长度超过的保持原样,否则补全
'''
n = num_full_scale_chars(s)
if n > max_num_full_scale_chars:
return s
else:
return s + ' ' * (max_num_full_scale_chars - n)
def len_display(s: str) -> int:
'''计算字符串的「显示长度」
- 🎯对中文和英文分别计算
- 📌【2024-05-27 11:47:24】目前以「半角空格」为单位
- 📌全角字符算两个
'''
# 计算全角字符(以及剩余的「半角字符」)
n_full_scale_chars = num_full_scale_chars(s)
n_half_scale_chars = len(s) - n_full_scale_chars
# 加权求和返回
return n_half_scale_chars + n_full_scale_chars * 2
def pad_display_spaces(s: str, max_num_display_chars: int, tail: bool = True) -> str:
'''填充字符串空格
- 📌【2024-05-27 11:45:19】目前认为其中字符只会出现「全角字符」与「半角字符」
- 📌不是全角字符,则为半角字符
- 📌全角字符算两个
'''
# 计算显示长度
l = len_display(s)
pad = (
''
if l > max_num_display_chars
else ' ' * (max_num_display_chars - l)
)
# 用半角空格补全
return s + pad if tail else pad + s
def InputIterator(
prompt: str,
*,
end_condition: Callable[[str], bool] = is_empty,
):
'''简单的用户输入迭代器
- 📌在迭代时请求用户输入
- 🚩默认为空字符串结束
'''
while True:
i = input(prompt)
if end_condition(i):
return
else:
yield i
def trim_left(s: str, prefix: str) -> str:
'''左侧整体性裁剪
- 📄trim_left('13223', '23') => '132'
- ⚠️不同于trim:会整个整个裁剪
'''
while s.startswith(prefix):
s = s[len(prefix):]
return s
def trim_right(s: str, suffix: str) -> str:
'''右侧整体性裁剪
- 📄trim_right('13123', '13') => '123'
- ⚠️不同于trim:会整个整个裁剪
'''
while s.endswith(suffix):
s = s[:-len(suffix)]
return s
VoidFunction = Callable[[], None]
'''无参数、无返回值的函数类型'''
def void(f: Callable) -> VoidFunction:
'''将一个函数转换为无返回值的函数'''
return lambda: (None, f())[0]
def is_in_or_contains(s1: Any, s2: Any):
'''判断两个对象是否包含或被包含'''
return s1 in s2 or s2 in s1