hutool-python 1.0.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.
- hutool/__init__.py +174 -0
- hutool/cache/__init__.py +7 -0
- hutool/cache/cache_util.py +47 -0
- hutool/cache/fifo_cache.py +87 -0
- hutool/cache/lfu_cache.py +129 -0
- hutool/cache/lru_cache.py +93 -0
- hutool/cache/timed_cache.py +115 -0
- hutool/captcha/__init__.py +3 -0
- hutool/captcha/captcha_util.py +215 -0
- hutool/core/__init__.py +23 -0
- hutool/core/_base.py +61 -0
- hutool/core/bean.py +214 -0
- hutool/core/codec.py +111 -0
- hutool/core/coll.py +635 -0
- hutool/core/date.py +1024 -0
- hutool/core/exceptions.py +66 -0
- hutool/core/io/__init__.py +0 -0
- hutool/core/io/data_size_util.py +79 -0
- hutool/core/io/file_name_util.py +111 -0
- hutool/core/io/file_util.py +650 -0
- hutool/core/io/io_util.py +133 -0
- hutool/core/io/path_util.py +247 -0
- hutool/core/io/resource_util.py +137 -0
- hutool/core/map.py +933 -0
- hutool/core/math_util.py +105 -0
- hutool/core/net.py +288 -0
- hutool/core/text/__init__.py +0 -0
- hutool/core/text/csv_util.py +54 -0
- hutool/core/text/str_builder.py +224 -0
- hutool/core/text/unicode_util.py +58 -0
- hutool/core/tree.py +242 -0
- hutool/core/util/__init__.py +63 -0
- hutool/core/util/array_util.py +503 -0
- hutool/core/util/boolean_util.py +124 -0
- hutool/core/util/charset_util.py +60 -0
- hutool/core/util/class_util.py +136 -0
- hutool/core/util/coordinate_util.py +186 -0
- hutool/core/util/credit_code_util.py +110 -0
- hutool/core/util/desensitized_util.py +194 -0
- hutool/core/util/enum_util.py +94 -0
- hutool/core/util/escape_util.py +97 -0
- hutool/core/util/hash_util.py +243 -0
- hutool/core/util/hex_util.py +140 -0
- hutool/core/util/id_util.py +147 -0
- hutool/core/util/idcard_util.py +300 -0
- hutool/core/util/number_util.py +720 -0
- hutool/core/util/object_util.py +294 -0
- hutool/core/util/page_util.py +61 -0
- hutool/core/util/phone_util.py +140 -0
- hutool/core/util/random_util.py +112 -0
- hutool/core/util/re_util.py +231 -0
- hutool/core/util/reflect_util.py +135 -0
- hutool/core/util/runtime_util.py +89 -0
- hutool/core/util/str_util.py +2320 -0
- hutool/core/util/system_util.py +62 -0
- hutool/core/util/url_util.py +232 -0
- hutool/core/util/version_util.py +41 -0
- hutool/core/util/xml_util.py +158 -0
- hutool/core/util/zip_util.py +126 -0
- hutool/cron/__init__.py +4 -0
- hutool/cron/cron_pattern.py +123 -0
- hutool/cron/cron_util.py +115 -0
- hutool/crypto/__init__.py +5 -0
- hutool/crypto/digest_util.py +167 -0
- hutool/crypto/secure_util.py +311 -0
- hutool/crypto/sign_util.py +74 -0
- hutool/dfa/__init__.py +3 -0
- hutool/dfa/sensitive_util.py +114 -0
- hutool/extra/__init__.py +6 -0
- hutool/extra/emoji_util.py +90 -0
- hutool/extra/pinyin_util.py +44 -0
- hutool/extra/qr_code_util.py +58 -0
- hutool/extra/template_util.py +41 -0
- hutool/http/__init__.py +6 -0
- hutool/http/html_util.py +88 -0
- hutool/http/http_request.py +188 -0
- hutool/http/http_response.py +139 -0
- hutool/http/http_util.py +237 -0
- hutool/json_util.py +251 -0
- hutool/jwt_util.py +57 -0
- hutool/setting/__init__.py +5 -0
- hutool/setting/props_util.py +79 -0
- hutool/setting/setting_util.py +80 -0
- hutool/setting/yaml_util.py +45 -0
- hutool_python-1.0.0.dist-info/LICENSE +127 -0
- hutool_python-1.0.0.dist-info/METADATA +438 -0
- hutool_python-1.0.0.dist-info/RECORD +89 -0
- hutool_python-1.0.0.dist-info/WHEEL +5 -0
- hutool_python-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
"""数组工具类,对应 Java cn.hutool.core.util.ArrayUtil"""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArrayUtil:
|
|
7
|
+
"""数组工具类,对应 Java cn.hutool.core.util.ArrayUtil"""
|
|
8
|
+
|
|
9
|
+
# ==================== 判断 ====================
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def is_empty(array) -> bool:
|
|
13
|
+
"""数组是否为空,None也视为空
|
|
14
|
+
|
|
15
|
+
:param array: 待检查的数组或列表
|
|
16
|
+
:return: 如果 array 为 None 或长度为 0 则返回 True
|
|
17
|
+
"""
|
|
18
|
+
if array is None:
|
|
19
|
+
return True
|
|
20
|
+
try:
|
|
21
|
+
return len(array) == 0
|
|
22
|
+
except TypeError:
|
|
23
|
+
# 不可迭代对象视为空
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def is_not_empty(array) -> bool:
|
|
28
|
+
"""数组是否为非空
|
|
29
|
+
|
|
30
|
+
:param array: 待检查的数组或列表
|
|
31
|
+
:return: 如果 array 非 None 且长度大于 0 则返回 True
|
|
32
|
+
"""
|
|
33
|
+
return not ArrayUtil.is_empty(array)
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def has_null(*args) -> bool:
|
|
37
|
+
"""是否有null元素
|
|
38
|
+
|
|
39
|
+
:param args: 任意数量的参数
|
|
40
|
+
:return: 如果存在任意一个参数为 None 则返回 True
|
|
41
|
+
"""
|
|
42
|
+
for arg in args:
|
|
43
|
+
if arg is None:
|
|
44
|
+
return True
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def is_all_null(*args) -> bool:
|
|
49
|
+
"""是否全部为null
|
|
50
|
+
|
|
51
|
+
:param args: 任意数量的参数
|
|
52
|
+
:return: 如果所有参数均为 None 则返回 True
|
|
53
|
+
"""
|
|
54
|
+
for arg in args:
|
|
55
|
+
if arg is not None:
|
|
56
|
+
return False
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def first_non_null(*args):
|
|
61
|
+
"""返回第一个非null值
|
|
62
|
+
|
|
63
|
+
:param args: 任意数量的参数
|
|
64
|
+
:return: 第一个不为 None 的值,全部为 None 则返回 None
|
|
65
|
+
"""
|
|
66
|
+
for arg in args:
|
|
67
|
+
if arg is not None:
|
|
68
|
+
return arg
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# ==================== 操作 ====================
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def append(array: list, *new_elements) -> list:
|
|
75
|
+
"""追加元素,返回新数组
|
|
76
|
+
|
|
77
|
+
:param array: 原始列表
|
|
78
|
+
:param new_elements: 要追加的元素
|
|
79
|
+
:return: 追加后的新列表
|
|
80
|
+
"""
|
|
81
|
+
if array is None:
|
|
82
|
+
return list(new_elements)
|
|
83
|
+
result = list(array)
|
|
84
|
+
result.extend(new_elements)
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def insert(array: list, index: int, *new_elements) -> list:
|
|
89
|
+
"""在指定位置插入元素
|
|
90
|
+
|
|
91
|
+
:param array: 原始列表
|
|
92
|
+
:param index: 插入位置索引,支持负索引
|
|
93
|
+
:param new_elements: 要插入的元素
|
|
94
|
+
:return: 插入后的新列表
|
|
95
|
+
:raises IndexError: 索引越界时抛出
|
|
96
|
+
"""
|
|
97
|
+
if array is None:
|
|
98
|
+
if index != 0:
|
|
99
|
+
raise IndexError(f"索引 {index} 越界,数组为 None")
|
|
100
|
+
return list(new_elements)
|
|
101
|
+
result = list(array)
|
|
102
|
+
# 处理负索引
|
|
103
|
+
actual_index = index
|
|
104
|
+
if actual_index < 0:
|
|
105
|
+
actual_index = len(result) + actual_index
|
|
106
|
+
if actual_index < 0 or actual_index > len(result):
|
|
107
|
+
raise IndexError(f"索引 {index} 越界,数组长度为 {len(array)}")
|
|
108
|
+
for i, elem in enumerate(new_elements):
|
|
109
|
+
result.insert(actual_index + i, elem)
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def remove(array: list, index: int) -> list:
|
|
114
|
+
"""移除指定位置元素
|
|
115
|
+
|
|
116
|
+
:param array: 原始列表
|
|
117
|
+
:param index: 要移除的位置索引,支持负索引
|
|
118
|
+
:return: 移除后的新列表
|
|
119
|
+
:raises IndexError: 索引越界时抛出
|
|
120
|
+
"""
|
|
121
|
+
if array is None:
|
|
122
|
+
raise IndexError("数组为 None,无法移除元素")
|
|
123
|
+
if index < 0 or index >= len(array):
|
|
124
|
+
raise IndexError(f"索引 {index} 越界,数组长度为 {len(array)}")
|
|
125
|
+
result = list(array)
|
|
126
|
+
result.pop(index)
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def remove_ele(array: list, element) -> list:
|
|
131
|
+
"""移除指定元素(第一个匹配)
|
|
132
|
+
|
|
133
|
+
:param array: 原始列表
|
|
134
|
+
:param element: 要移除的元素
|
|
135
|
+
:return: 移除后的新列表,如果元素不存在则返回原列表的副本
|
|
136
|
+
"""
|
|
137
|
+
if array is None:
|
|
138
|
+
return []
|
|
139
|
+
result = list(array)
|
|
140
|
+
try:
|
|
141
|
+
result.remove(element)
|
|
142
|
+
except ValueError:
|
|
143
|
+
pass
|
|
144
|
+
return result
|
|
145
|
+
|
|
146
|
+
# ==================== 查找 ====================
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def index_of(array, value) -> int:
|
|
150
|
+
"""查找元素位置,不存在返回-1
|
|
151
|
+
|
|
152
|
+
:param array: 要搜索的列表或可迭代对象
|
|
153
|
+
:param value: 要查找的值
|
|
154
|
+
:return: 元素首次出现的索引,不存在返回 -1
|
|
155
|
+
"""
|
|
156
|
+
if array is None:
|
|
157
|
+
return -1
|
|
158
|
+
try:
|
|
159
|
+
for i, item in enumerate(array):
|
|
160
|
+
if item == value:
|
|
161
|
+
return i
|
|
162
|
+
except TypeError:
|
|
163
|
+
return -1
|
|
164
|
+
return -1
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def last_index_of(array, value) -> int:
|
|
168
|
+
"""从后向前查找
|
|
169
|
+
|
|
170
|
+
:param array: 要搜索的列表或可迭代对象
|
|
171
|
+
:param value: 要查找的值
|
|
172
|
+
:return: 元素最后出现的索引,不存在返回 -1
|
|
173
|
+
"""
|
|
174
|
+
if array is None:
|
|
175
|
+
return -1
|
|
176
|
+
result = -1
|
|
177
|
+
try:
|
|
178
|
+
for i, item in enumerate(array):
|
|
179
|
+
if item == value:
|
|
180
|
+
result = i
|
|
181
|
+
except TypeError:
|
|
182
|
+
return -1
|
|
183
|
+
return result
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def contains(array, value) -> bool:
|
|
187
|
+
"""是否包含指定元素
|
|
188
|
+
|
|
189
|
+
:param array: 要搜索的列表或可迭代对象
|
|
190
|
+
:param value: 要查找的值
|
|
191
|
+
:return: 如果包含该元素则返回 True
|
|
192
|
+
"""
|
|
193
|
+
return ArrayUtil.index_of(array, value) >= 0
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def contains_any(array, *values) -> bool:
|
|
197
|
+
"""是否包含任意一个
|
|
198
|
+
|
|
199
|
+
:param array: 要搜索的列表或可迭代对象
|
|
200
|
+
:param values: 要查找的值(可多个)
|
|
201
|
+
:return: 如果包含任意一个值则返回 True
|
|
202
|
+
"""
|
|
203
|
+
if array is None:
|
|
204
|
+
return False
|
|
205
|
+
for v in values:
|
|
206
|
+
if ArrayUtil.contains(array, v):
|
|
207
|
+
return True
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def contains_all(array, *values) -> bool:
|
|
212
|
+
"""是否包含全部
|
|
213
|
+
|
|
214
|
+
:param array: 要搜索的列表或可迭代对象
|
|
215
|
+
:param values: 要查找的值(可多个)
|
|
216
|
+
:return: 如果包含全部值则返回 True
|
|
217
|
+
"""
|
|
218
|
+
if array is None:
|
|
219
|
+
return len(values) == 0
|
|
220
|
+
for v in values:
|
|
221
|
+
if not ArrayUtil.contains(array, v):
|
|
222
|
+
return False
|
|
223
|
+
return True
|
|
224
|
+
|
|
225
|
+
# ==================== 子数组 ====================
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def sub(array: list, start: int, end: int) -> list:
|
|
229
|
+
"""获取子数组,支持负索引
|
|
230
|
+
|
|
231
|
+
:param array: 原始列表
|
|
232
|
+
:param start: 起始索引(包含),支持负索引
|
|
233
|
+
:param end: 结束索引(不包含),支持负索引
|
|
234
|
+
:return: 子列表
|
|
235
|
+
:raises IndexError: 索引越界时抛出
|
|
236
|
+
"""
|
|
237
|
+
if array is None:
|
|
238
|
+
raise IndexError("数组为 None,无法获取子数组")
|
|
239
|
+
length = len(array)
|
|
240
|
+
# 处理负索引
|
|
241
|
+
actual_start = start if start >= 0 else max(length + start, 0)
|
|
242
|
+
actual_end = end if end >= 0 else max(length + end, 0)
|
|
243
|
+
# 边界修正
|
|
244
|
+
actual_start = min(actual_start, length)
|
|
245
|
+
actual_end = min(actual_end, length)
|
|
246
|
+
if actual_start > actual_end:
|
|
247
|
+
raise IndexError(f"起始索引 {start} 大于结束索引 {end}")
|
|
248
|
+
return list(array[actual_start:actual_end])
|
|
249
|
+
|
|
250
|
+
# ==================== 转换 ====================
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def join(array, conjunction: str) -> str:
|
|
254
|
+
"""数组转字符串,用连接符连接
|
|
255
|
+
|
|
256
|
+
:param array: 要连接的列表或可迭代对象
|
|
257
|
+
:param conjunction: 连接符
|
|
258
|
+
:return: 连接后的字符串,空数组返回空字符串
|
|
259
|
+
"""
|
|
260
|
+
if array is None:
|
|
261
|
+
return ""
|
|
262
|
+
return conjunction.join(str(item) for item in array)
|
|
263
|
+
|
|
264
|
+
@staticmethod
|
|
265
|
+
def zip_list(keys: list, values: list) -> dict:
|
|
266
|
+
"""两个列表合并为字典
|
|
267
|
+
|
|
268
|
+
:param keys: 键列表
|
|
269
|
+
:param values: 值列表
|
|
270
|
+
:return: 合并后的字典,长度以较短的列表为准
|
|
271
|
+
"""
|
|
272
|
+
if keys is None or values is None:
|
|
273
|
+
return {}
|
|
274
|
+
return dict(zip(keys, values))
|
|
275
|
+
|
|
276
|
+
@staticmethod
|
|
277
|
+
def to_list(iterable) -> list:
|
|
278
|
+
"""转为列表
|
|
279
|
+
|
|
280
|
+
:param iterable: 可迭代对象
|
|
281
|
+
:return: 转换后的列表,None 返回空列表
|
|
282
|
+
"""
|
|
283
|
+
if iterable is None:
|
|
284
|
+
return []
|
|
285
|
+
if isinstance(iterable, list):
|
|
286
|
+
return list(iterable)
|
|
287
|
+
try:
|
|
288
|
+
return list(iterable)
|
|
289
|
+
except TypeError:
|
|
290
|
+
return [iterable]
|
|
291
|
+
|
|
292
|
+
@staticmethod
|
|
293
|
+
def reverse(array: list) -> list:
|
|
294
|
+
"""反转数组,返回新数组
|
|
295
|
+
|
|
296
|
+
:param array: 原始列表
|
|
297
|
+
:return: 反转后的新列表
|
|
298
|
+
"""
|
|
299
|
+
if array is None:
|
|
300
|
+
return []
|
|
301
|
+
return list(reversed(array))
|
|
302
|
+
|
|
303
|
+
# ==================== 工具 ====================
|
|
304
|
+
|
|
305
|
+
@staticmethod
|
|
306
|
+
def shuffle(array: list) -> list:
|
|
307
|
+
"""随机打乱,返回新数组
|
|
308
|
+
|
|
309
|
+
:param array: 原始列表
|
|
310
|
+
:return: 随机打乱后的新列表
|
|
311
|
+
"""
|
|
312
|
+
import random
|
|
313
|
+
|
|
314
|
+
if array is None:
|
|
315
|
+
return []
|
|
316
|
+
result = list(array)
|
|
317
|
+
random.shuffle(result)
|
|
318
|
+
return result
|
|
319
|
+
|
|
320
|
+
@staticmethod
|
|
321
|
+
def swap(array: list, index1: int, index2: int) -> list:
|
|
322
|
+
"""交换两个位置的元素
|
|
323
|
+
|
|
324
|
+
:param array: 原始列表
|
|
325
|
+
:param index1: 第一个位置索引
|
|
326
|
+
:param index2: 第二个位置索引
|
|
327
|
+
:return: 交换后的新列表
|
|
328
|
+
:raises IndexError: 索引越界时抛出
|
|
329
|
+
"""
|
|
330
|
+
if array is None:
|
|
331
|
+
raise IndexError("数组为 None,无法交换元素")
|
|
332
|
+
length = len(array)
|
|
333
|
+
# 处理负索引
|
|
334
|
+
i1 = index1 if index1 >= 0 else length + index1
|
|
335
|
+
i2 = index2 if index2 >= 0 else length + index2
|
|
336
|
+
if i1 < 0 or i1 >= length:
|
|
337
|
+
raise IndexError(f"索引 {index1} 越界,数组长度为 {length}")
|
|
338
|
+
if i2 < 0 or i2 >= length:
|
|
339
|
+
raise IndexError(f"索引 {index2} 越界,数组长度为 {length}")
|
|
340
|
+
result = list(array)
|
|
341
|
+
result[i1], result[i2] = result[i2], result[i1]
|
|
342
|
+
return result
|
|
343
|
+
|
|
344
|
+
@staticmethod
|
|
345
|
+
def length(array) -> int:
|
|
346
|
+
"""获取数组长度
|
|
347
|
+
|
|
348
|
+
:param array: 数组或列表,None 返回 0
|
|
349
|
+
:return: 数组长度
|
|
350
|
+
"""
|
|
351
|
+
if array is None:
|
|
352
|
+
return 0
|
|
353
|
+
try:
|
|
354
|
+
return len(array)
|
|
355
|
+
except TypeError:
|
|
356
|
+
return 0
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def min(array) -> Any:
|
|
360
|
+
"""取最小值
|
|
361
|
+
|
|
362
|
+
:param array: 非空列表或可迭代对象
|
|
363
|
+
:return: 最小值
|
|
364
|
+
:raises ValueError: 数组为空时抛出
|
|
365
|
+
"""
|
|
366
|
+
if array is None or (hasattr(array, "__len__") and len(array) == 0):
|
|
367
|
+
raise ValueError("数组为空,无法获取最小值")
|
|
368
|
+
return min(array)
|
|
369
|
+
|
|
370
|
+
@staticmethod
|
|
371
|
+
def max(array) -> Any:
|
|
372
|
+
"""取最大值
|
|
373
|
+
|
|
374
|
+
:param array: 非空列表或可迭代对象
|
|
375
|
+
:return: 最大值
|
|
376
|
+
:raises ValueError: 数组为空时抛出
|
|
377
|
+
"""
|
|
378
|
+
if array is None or (hasattr(array, "__len__") and len(array) == 0):
|
|
379
|
+
raise ValueError("数组为空,无法获取最大值")
|
|
380
|
+
return max(array)
|
|
381
|
+
|
|
382
|
+
@staticmethod
|
|
383
|
+
def empty_count(*args) -> int:
|
|
384
|
+
"""空值数量
|
|
385
|
+
|
|
386
|
+
空值定义:None、空字符串、空列表/元组/字典/集合等。
|
|
387
|
+
|
|
388
|
+
:param args: 任意数量的参数
|
|
389
|
+
:return: 空值的个数
|
|
390
|
+
"""
|
|
391
|
+
count = 0
|
|
392
|
+
for arg in args:
|
|
393
|
+
if arg is None:
|
|
394
|
+
count += 1
|
|
395
|
+
elif isinstance(arg, (str, list, tuple, dict, set, frozenset, bytes)):
|
|
396
|
+
if len(arg) == 0:
|
|
397
|
+
count += 1
|
|
398
|
+
return count
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def has_empty(*args) -> bool:
|
|
402
|
+
"""是否有空值
|
|
403
|
+
|
|
404
|
+
空值定义:None、空字符串、空列表/元组/字典/集合等。
|
|
405
|
+
|
|
406
|
+
:param args: 任意数量的参数
|
|
407
|
+
:return: 如果存在任意一个空值则返回 True
|
|
408
|
+
"""
|
|
409
|
+
for arg in args:
|
|
410
|
+
if arg is None:
|
|
411
|
+
return True
|
|
412
|
+
if isinstance(arg, (str, list, tuple, dict, set, frozenset, bytes)):
|
|
413
|
+
if len(arg) == 0:
|
|
414
|
+
return True
|
|
415
|
+
return False
|
|
416
|
+
|
|
417
|
+
@staticmethod
|
|
418
|
+
def filter(array: list, filter_func: Callable[[Any], bool]) -> list:
|
|
419
|
+
"""过滤数组
|
|
420
|
+
|
|
421
|
+
:param array: 原始列表
|
|
422
|
+
:param filter_func: 过滤函数,返回 True 保留,False 过滤
|
|
423
|
+
:return: 过滤后的新列表
|
|
424
|
+
"""
|
|
425
|
+
if array is None:
|
|
426
|
+
return []
|
|
427
|
+
return [item for item in array if filter_func(item)]
|
|
428
|
+
|
|
429
|
+
@staticmethod
|
|
430
|
+
def map(array: list, map_func: Callable[[Any], Any]) -> list:
|
|
431
|
+
"""映射数组
|
|
432
|
+
|
|
433
|
+
:param array: 原始列表
|
|
434
|
+
:param map_func: 映射函数,对每个元素进行转换
|
|
435
|
+
:return: 映射后的新列表
|
|
436
|
+
"""
|
|
437
|
+
if array is None:
|
|
438
|
+
return []
|
|
439
|
+
return [map_func(item) for item in array]
|
|
440
|
+
|
|
441
|
+
@staticmethod
|
|
442
|
+
def flatten(nested_list) -> list:
|
|
443
|
+
"""展平嵌套列表
|
|
444
|
+
|
|
445
|
+
将多层嵌套的列表展平为一维列表。字符串和字节类型不会被展开。
|
|
446
|
+
|
|
447
|
+
:param nested_list: 嵌套列表
|
|
448
|
+
:return: 展平后的一维列表
|
|
449
|
+
"""
|
|
450
|
+
result: list = []
|
|
451
|
+
|
|
452
|
+
def _flatten(item):
|
|
453
|
+
if isinstance(item, (str, bytes)):
|
|
454
|
+
result.append(item)
|
|
455
|
+
return
|
|
456
|
+
try:
|
|
457
|
+
for sub_item in item:
|
|
458
|
+
_flatten(sub_item)
|
|
459
|
+
except TypeError:
|
|
460
|
+
result.append(item)
|
|
461
|
+
|
|
462
|
+
if nested_list is not None:
|
|
463
|
+
_flatten(nested_list)
|
|
464
|
+
return result
|
|
465
|
+
|
|
466
|
+
@staticmethod
|
|
467
|
+
def distinct(array: list) -> list:
|
|
468
|
+
"""去重,保持顺序
|
|
469
|
+
|
|
470
|
+
:param array: 原始列表
|
|
471
|
+
:return: 去重后的新列表,保持原有顺序
|
|
472
|
+
"""
|
|
473
|
+
if array is None:
|
|
474
|
+
return []
|
|
475
|
+
seen = set()
|
|
476
|
+
result = []
|
|
477
|
+
for item in array:
|
|
478
|
+
# 尝试用 hash 加速查找,不可 hash 的元素回退到线性查找
|
|
479
|
+
try:
|
|
480
|
+
if item not in seen:
|
|
481
|
+
seen.add(item)
|
|
482
|
+
result.append(item)
|
|
483
|
+
except TypeError:
|
|
484
|
+
# item 不可 hash(如 list、dict),使用线性查找
|
|
485
|
+
if item not in result:
|
|
486
|
+
result.append(item)
|
|
487
|
+
return result
|
|
488
|
+
|
|
489
|
+
@staticmethod
|
|
490
|
+
def fill(value, size: int) -> list:
|
|
491
|
+
"""用指定值填充指定大小的列表
|
|
492
|
+
|
|
493
|
+
注意:如果 value 是可变对象(如 list、dict),列表中的每个元素将是同一个引用。
|
|
494
|
+
如需独立副本,请对每个元素单独复制。
|
|
495
|
+
|
|
496
|
+
:param value: 填充的值
|
|
497
|
+
:param size: 列表大小
|
|
498
|
+
:return: 填充后的列表
|
|
499
|
+
:raises ValueError: size 为负数时抛出
|
|
500
|
+
"""
|
|
501
|
+
if size < 0:
|
|
502
|
+
raise ValueError(f"大小不能为负数: {size}")
|
|
503
|
+
return [value] * size
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""布尔工具类,对应 Java cn.hutool.core.util.BooleanUtil"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BooleanUtil:
|
|
7
|
+
"""布尔工具类,对应 Java cn.hutool.core.util.BooleanUtil"""
|
|
8
|
+
|
|
9
|
+
# 用于字符串解析时判定为 True 的值(小写)
|
|
10
|
+
_TRUE_STRINGS = frozenset({"true", "yes", "1", "on"})
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def is_true(bool_value: Optional[bool]) -> bool:
|
|
14
|
+
"""是否为 True,None 返回 False。
|
|
15
|
+
|
|
16
|
+
:param bool_value: 待判断的布尔值
|
|
17
|
+
:return: 若为 True 返回 True,否则(包括 None)返回 False
|
|
18
|
+
"""
|
|
19
|
+
return bool_value is True
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def is_false(bool_value: Optional[bool]) -> bool:
|
|
23
|
+
"""是否为 False,None 返回 False。
|
|
24
|
+
|
|
25
|
+
:param bool_value: 待判断的布尔值
|
|
26
|
+
:return: 若为 False 返回 True,否则(包括 None)返回 False
|
|
27
|
+
"""
|
|
28
|
+
return bool_value is False
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def to_int(bool_value: Optional[bool]) -> int:
|
|
32
|
+
"""布尔转 int,True -> 1,False -> 0,None -> 0。
|
|
33
|
+
|
|
34
|
+
:param bool_value: 待转换的布尔值
|
|
35
|
+
:return: 对应的 int 值
|
|
36
|
+
"""
|
|
37
|
+
if bool_value is True:
|
|
38
|
+
return 1
|
|
39
|
+
return 0
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def int_to_boolean(int_value: Optional[int]) -> bool:
|
|
43
|
+
"""int 转布尔,非 0 为 True。
|
|
44
|
+
|
|
45
|
+
:param int_value: 待转换的 int 值
|
|
46
|
+
:return: 非 0 返回 True,0 或 None 返回 False
|
|
47
|
+
"""
|
|
48
|
+
if int_value is None:
|
|
49
|
+
return False
|
|
50
|
+
return int_value != 0
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def and_(*values: bool) -> bool:
|
|
54
|
+
"""逻辑与运算,所有值均为 True 时返回 True。
|
|
55
|
+
|
|
56
|
+
无参数时返回 True(与 Python 内置 all 行为一致)。
|
|
57
|
+
|
|
58
|
+
:param values: 一组布尔值
|
|
59
|
+
:return: 所有值均为 True 时返回 True,否则返回 False
|
|
60
|
+
"""
|
|
61
|
+
return all(values)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def or_(*values: bool) -> bool:
|
|
65
|
+
"""逻辑或运算,任一值为 True 时返回 True。
|
|
66
|
+
|
|
67
|
+
无参数时返回 False(与 Python 内置 any 行为一致)。
|
|
68
|
+
|
|
69
|
+
:param values: 一组布尔值
|
|
70
|
+
:return: 任一值为 True 时返回 True,否则返回 False
|
|
71
|
+
"""
|
|
72
|
+
return any(values)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def xor(*values: bool) -> bool:
|
|
76
|
+
"""逻辑异或运算,奇数个 True 时返回 True。
|
|
77
|
+
|
|
78
|
+
计算方式:对所有 True 的个数取模,奇数个 True 结果为 True。
|
|
79
|
+
|
|
80
|
+
:param values: 一组布尔值
|
|
81
|
+
:return: 奇数个 True 时返回 True,否则返回 False
|
|
82
|
+
"""
|
|
83
|
+
count = sum(1 for v in values if v)
|
|
84
|
+
return count % 2 == 1
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def negate(bool_value: bool) -> bool:
|
|
88
|
+
"""取反。
|
|
89
|
+
|
|
90
|
+
:param bool_value: 待取反的布尔值
|
|
91
|
+
:return: 取反后的结果
|
|
92
|
+
"""
|
|
93
|
+
return not bool_value
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def to_str(
|
|
97
|
+
bool_value: Optional[bool],
|
|
98
|
+
true_str: str = "true",
|
|
99
|
+
false_str: str = "false",
|
|
100
|
+
) -> str:
|
|
101
|
+
"""布尔转字符串。
|
|
102
|
+
|
|
103
|
+
:param bool_value: 待转换的布尔值,None 视为 False
|
|
104
|
+
:param true_str: True 对应的字符串,默认为 "true"
|
|
105
|
+
:param false_str: False 对应的字符串,默认为 "false"
|
|
106
|
+
:return: 对应的字符串
|
|
107
|
+
"""
|
|
108
|
+
if bool_value is True:
|
|
109
|
+
return true_str
|
|
110
|
+
return false_str
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def parse(str_value: Optional[str]) -> bool:
|
|
114
|
+
"""字符串解析为布尔值。
|
|
115
|
+
|
|
116
|
+
以下值(忽略大小写)解析为 True: "true", "yes", "1", "on"。
|
|
117
|
+
其他所有值(包括 None)解析为 False。
|
|
118
|
+
|
|
119
|
+
:param str_value: 待解析的字符串
|
|
120
|
+
:return: 解析后的布尔值
|
|
121
|
+
"""
|
|
122
|
+
if str_value is None:
|
|
123
|
+
return False
|
|
124
|
+
return str_value.strip().lower() in BooleanUtil._TRUE_STRINGS
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""字符集工具类"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import locale
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CharsetUtil:
|
|
10
|
+
"""字符集工具类"""
|
|
11
|
+
|
|
12
|
+
UTF_8 = "utf-8"
|
|
13
|
+
GBK = "gbk"
|
|
14
|
+
ISO_8859_1 = "iso-8859-1"
|
|
15
|
+
US_ASCII = "us-ascii"
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def charset(charset_name: str) -> str:
|
|
19
|
+
"""获取字符集名称,None返回UTF-8"""
|
|
20
|
+
if charset_name is None:
|
|
21
|
+
return CharsetUtil.UTF_8
|
|
22
|
+
return charset_name.lower()
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def default_charset() -> str:
|
|
26
|
+
"""获取系统默认字符集"""
|
|
27
|
+
# 优先使用preferredencoding,回退到locale
|
|
28
|
+
encoding = sys.getdefaultencoding()
|
|
29
|
+
if encoding:
|
|
30
|
+
return encoding
|
|
31
|
+
loc = locale.getpreferredencoding(False)
|
|
32
|
+
return loc if loc else CharsetUtil.UTF_8
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def convert(source: bytes, src_charset: str, dest_charset: str) -> bytes:
|
|
36
|
+
"""字节数据转码"""
|
|
37
|
+
if src_charset is None:
|
|
38
|
+
src_charset = CharsetUtil.UTF_8
|
|
39
|
+
if dest_charset is None:
|
|
40
|
+
dest_charset = CharsetUtil.UTF_8
|
|
41
|
+
text = source.decode(src_charset)
|
|
42
|
+
return text.encode(dest_charset)
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def convert_str(source: str, src_charset: str, dest_charset: str) -> str:
|
|
46
|
+
"""字符串转码"""
|
|
47
|
+
if src_charset is None:
|
|
48
|
+
src_charset = CharsetUtil.UTF_8
|
|
49
|
+
if dest_charset is None:
|
|
50
|
+
dest_charset = CharsetUtil.UTF_8
|
|
51
|
+
byte_data = source.encode(src_charset)
|
|
52
|
+
return byte_data.decode(dest_charset)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def clean_bom(content: str) -> str:
|
|
56
|
+
"""清理BOM头"""
|
|
57
|
+
# UTF-8 BOM:
|
|
58
|
+
if content and content[0] == "":
|
|
59
|
+
return content[1:]
|
|
60
|
+
return content
|