pyxllib 0.3.197__py3-none-any.whl → 0.3.200__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.
- pyxllib/__init__.py +21 -21
- pyxllib/algo/__init__.py +8 -8
- pyxllib/algo/disjoint.py +54 -54
- pyxllib/algo/geo.py +541 -541
- pyxllib/algo/intervals.py +964 -964
- pyxllib/algo/matcher.py +389 -389
- pyxllib/algo/newbie.py +166 -166
- pyxllib/algo/pupil.py +629 -629
- pyxllib/algo/shapelylib.py +67 -67
- pyxllib/algo/specialist.py +241 -241
- pyxllib/algo/stat.py +494 -494
- pyxllib/algo/treelib.py +149 -149
- pyxllib/algo/unitlib.py +66 -66
- pyxllib/autogui/__init__.py +5 -5
- pyxllib/autogui/activewin.py +246 -246
- pyxllib/autogui/all.py +9 -9
- pyxllib/autogui/autogui.py +852 -852
- pyxllib/autogui/uiautolib.py +362 -362
- pyxllib/autogui/virtualkey.py +102 -102
- pyxllib/autogui/wechat.py +827 -827
- pyxllib/autogui/wechat_msg.py +421 -421
- pyxllib/autogui/wxautolib.py +84 -84
- pyxllib/cv/__init__.py +5 -5
- pyxllib/cv/expert.py +267 -267
- pyxllib/cv/imfile.py +159 -159
- pyxllib/cv/imhash.py +39 -39
- pyxllib/cv/pupil.py +9 -9
- pyxllib/cv/rgbfmt.py +1525 -1525
- pyxllib/cv/slidercaptcha.py +137 -137
- pyxllib/cv/trackbartools.py +251 -251
- pyxllib/cv/xlcvlib.py +1040 -1040
- pyxllib/cv/xlpillib.py +423 -423
- pyxllib/data/echarts.py +240 -240
- pyxllib/data/jsonlib.py +89 -89
- pyxllib/data/oss.py +72 -72
- pyxllib/data/pglib.py +1127 -1127
- pyxllib/data/sqlite.py +568 -568
- pyxllib/data/sqllib.py +297 -297
- pyxllib/ext/JLineViewer.py +505 -505
- pyxllib/ext/__init__.py +6 -6
- pyxllib/ext/demolib.py +246 -246
- pyxllib/ext/drissionlib.py +277 -277
- pyxllib/ext/kq5034lib.py +12 -12
- pyxllib/ext/old.py +663 -663
- pyxllib/ext/qt.py +449 -449
- pyxllib/ext/robustprocfile.py +497 -497
- pyxllib/ext/seleniumlib.py +76 -76
- pyxllib/ext/tk.py +173 -173
- pyxllib/ext/unixlib.py +827 -827
- pyxllib/ext/utools.py +351 -351
- pyxllib/ext/webhook.py +124 -119
- pyxllib/ext/win32lib.py +40 -40
- pyxllib/ext/wjxlib.py +88 -88
- pyxllib/ext/wpsapi.py +124 -124
- pyxllib/ext/xlwork.py +9 -9
- pyxllib/ext/yuquelib.py +1105 -1105
- pyxllib/file/__init__.py +17 -17
- pyxllib/file/docxlib.py +761 -761
- pyxllib/file/gitlib.py +309 -309
- pyxllib/file/libreoffice.py +165 -165
- pyxllib/file/movielib.py +148 -148
- pyxllib/file/newbie.py +10 -10
- pyxllib/file/onenotelib.py +1469 -1469
- pyxllib/file/packlib/__init__.py +330 -330
- pyxllib/file/packlib/zipfile.py +2441 -2441
- pyxllib/file/pdflib.py +426 -426
- pyxllib/file/pupil.py +185 -185
- pyxllib/file/specialist/__init__.py +685 -685
- pyxllib/file/specialist/dirlib.py +799 -799
- pyxllib/file/specialist/download.py +193 -193
- pyxllib/file/specialist/filelib.py +2829 -2829
- pyxllib/file/xlsxlib.py +3131 -3131
- pyxllib/file/xlsyncfile.py +341 -341
- pyxllib/prog/__init__.py +5 -5
- pyxllib/prog/cachetools.py +64 -64
- pyxllib/prog/deprecatedlib.py +233 -233
- pyxllib/prog/filelock.py +42 -42
- pyxllib/prog/ipyexec.py +253 -253
- pyxllib/prog/multiprogs.py +940 -940
- pyxllib/prog/newbie.py +451 -451
- pyxllib/prog/pupil.py +1197 -1197
- pyxllib/prog/sitepackages.py +33 -33
- pyxllib/prog/specialist/__init__.py +391 -391
- pyxllib/prog/specialist/bc.py +203 -203
- pyxllib/prog/specialist/browser.py +497 -497
- pyxllib/prog/specialist/common.py +347 -347
- pyxllib/prog/specialist/datetime.py +198 -198
- pyxllib/prog/specialist/tictoc.py +240 -240
- pyxllib/prog/specialist/xllog.py +180 -180
- pyxllib/prog/xlosenv.py +108 -108
- pyxllib/stdlib/__init__.py +17 -17
- pyxllib/stdlib/tablepyxl/__init__.py +10 -10
- pyxllib/stdlib/tablepyxl/style.py +303 -303
- pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
- pyxllib/text/__init__.py +8 -8
- pyxllib/text/ahocorasick.py +39 -39
- pyxllib/text/airscript.js +744 -744
- pyxllib/text/charclasslib.py +121 -121
- pyxllib/text/jiebalib.py +267 -267
- pyxllib/text/jinjalib.py +32 -32
- pyxllib/text/jsa_ai_prompt.md +271 -271
- pyxllib/text/jscode.py +922 -922
- pyxllib/text/latex/__init__.py +158 -158
- pyxllib/text/levenshtein.py +303 -303
- pyxllib/text/nestenv.py +1215 -1215
- pyxllib/text/newbie.py +300 -300
- pyxllib/text/pupil/__init__.py +8 -8
- pyxllib/text/pupil/common.py +1121 -1121
- pyxllib/text/pupil/xlalign.py +326 -326
- pyxllib/text/pycode.py +47 -47
- pyxllib/text/specialist/__init__.py +8 -8
- pyxllib/text/specialist/common.py +112 -112
- pyxllib/text/specialist/ptag.py +186 -186
- pyxllib/text/spellchecker.py +172 -172
- pyxllib/text/templates/echart_base.html +10 -10
- pyxllib/text/templates/highlight_code.html +16 -16
- pyxllib/text/templates/latex_editor.html +102 -102
- pyxllib/text/vbacode.py +17 -17
- pyxllib/text/xmllib.py +747 -747
- pyxllib/xl.py +42 -39
- pyxllib/xlcv.py +17 -17
- {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/METADATA +1 -1
- pyxllib-0.3.200.dist-info/RECORD +126 -0
- {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/licenses/LICENSE +190 -190
- pyxllib-0.3.197.dist-info/RECORD +0 -126
- {pyxllib-0.3.197.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +0 -0
pyxllib/prog/newbie.py
CHANGED
@@ -1,451 +1,451 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Date : 2021/06/06 10:51
|
6
|
-
|
7
|
-
|
8
|
-
def typename(c):
|
9
|
-
""" 简化输出的type类型
|
10
|
-
|
11
|
-
>>> typename(123)
|
12
|
-
'int'
|
13
|
-
"""
|
14
|
-
return str(type(c))[8:-2]
|
15
|
-
|
16
|
-
|
17
|
-
class SingletonForEveryClass(type):
|
18
|
-
""" 注意如果A是单例类,B从A继承,那么实际有且仅有A、B两个不同的实例对象 """
|
19
|
-
_instances = {}
|
20
|
-
|
21
|
-
def __call__(cls, *args, **kwargs):
|
22
|
-
tag = f'{cls}'
|
23
|
-
# 其实转字符串来判断是不太严谨的,有些类型字符串后的显示效果是一模一样的
|
24
|
-
# dprint(tag)
|
25
|
-
if tag not in cls._instances:
|
26
|
-
cls._instances[tag] = super(SingletonForEveryClass, cls).__call__(*args, **kwargs)
|
27
|
-
return cls._instances[tag]
|
28
|
-
|
29
|
-
|
30
|
-
class SingletonForEveryInitArgs(type):
|
31
|
-
""" Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
|
32
|
-
|
33
|
-
注意!注意!注意!重要的事说三遍!
|
34
|
-
这里的单例类不是传统意义上的单例类。
|
35
|
-
传统意义的单例类,不管用怎样不同的初始化参数创建对象,永远都只有最初的那个对象类。
|
36
|
-
但是这个单例类,为每种不同的参数创建形式,都构造了一个对象。
|
37
|
-
"""
|
38
|
-
_instances = {}
|
39
|
-
|
40
|
-
def __call__(cls, *args, **kwargs):
|
41
|
-
tag = f'{cls}{args}{kwargs}' # id加上所有参数的影响来控制单例类
|
42
|
-
# 其实转字符串来判断是不太严谨的,有些类型字符串后的显示效果是一模一样的
|
43
|
-
# dprint(tag)
|
44
|
-
if tag not in cls._instances:
|
45
|
-
cls._instances[tag] = super(SingletonForEveryInitArgs, cls).__call__(*args, **kwargs)
|
46
|
-
return cls._instances[tag]
|
47
|
-
|
48
|
-
|
49
|
-
def xlbool(v):
|
50
|
-
""" 有些类型不能直接判断真假,例如具有多值的np.array,df等
|
51
|
-
|
52
|
-
这些有歧义的情况,在我的mybool里暂时都判断为True,如果有需要,需要精细化判断,可以扩展自己的npbool、dfbool
|
53
|
-
"""
|
54
|
-
try:
|
55
|
-
return bool(v)
|
56
|
-
except ValueError:
|
57
|
-
return True
|
58
|
-
|
59
|
-
|
60
|
-
def document(func):
|
61
|
-
"""文档函数装饰器
|
62
|
-
用该装饰器器时,表明一个函数是用伪代码在表示一系列的操作逻辑,不能直接拿来执行的
|
63
|
-
很可能是一套半自动化工具
|
64
|
-
"""
|
65
|
-
|
66
|
-
def wrapper(*args):
|
67
|
-
raise RuntimeError(f'函数:{func.__name__} 是一个伪代码流程示例文档,不能直接运行')
|
68
|
-
|
69
|
-
return wrapper
|
70
|
-
|
71
|
-
|
72
|
-
class RunOnlyOnce:
|
73
|
-
""" 被装饰的函数,不同的参数输入形式,只会被执行一次,
|
74
|
-
|
75
|
-
重复执行时会从内存直接调用上次相同参数调用下的运行的结果
|
76
|
-
可以使用reset成员函数重置,下一次调用该函数时则会重新执行
|
77
|
-
|
78
|
-
文档:https://www.yuque.com/xlpr/pyxllib/RunOnlyOnce
|
79
|
-
|
80
|
-
使用好该装饰器,可能让一些动态规划dp、搜索问题变得更简洁,
|
81
|
-
以及一些配置文件操作,可以做到只读一遍
|
82
|
-
"""
|
83
|
-
|
84
|
-
def __init__(self, func, distinct_args=True):
|
85
|
-
"""
|
86
|
-
:param func: 封装的函数
|
87
|
-
:param distinct_args: 默认不同输入参数形式,都会保存一个结果
|
88
|
-
设为False,则不管何种参数形式,函数就真的只会保存第一次运行的结果
|
89
|
-
"""
|
90
|
-
self.func = func
|
91
|
-
self.distinct_args = distinct_args
|
92
|
-
self.results = {}
|
93
|
-
|
94
|
-
@classmethod
|
95
|
-
def decorator(cls, distinct_args=True):
|
96
|
-
""" 作为装饰器的时候,如果要设置参数,要用这个接口 """
|
97
|
-
|
98
|
-
def wrap(func):
|
99
|
-
return cls(func, distinct_args)
|
100
|
-
|
101
|
-
return wrap
|
102
|
-
|
103
|
-
def __call__(self, *args, **kwargs):
|
104
|
-
tag = f'{args}{kwargs}' if self.distinct_args else ''
|
105
|
-
# TODO 思考更严谨,考虑了值类型的tag标记
|
106
|
-
# 目前的tag规则,是必要不充分条件。还可以使用id,则是充分不必要条件
|
107
|
-
# 能找到充要条件来做是最好的,不行的话,也应该用更严谨的充分条件来做
|
108
|
-
# TODO kwargs的顺序应该是没影响的,要去掉顺序干扰
|
109
|
-
if tag not in self.results:
|
110
|
-
self.results[tag] = self.func(*args, **kwargs)
|
111
|
-
return self.results[tag]
|
112
|
-
|
113
|
-
def reset(self):
|
114
|
-
self.results = {}
|
115
|
-
|
116
|
-
|
117
|
-
def len_in_dim2(arr):
|
118
|
-
""" 计算类List结构在第2维上的最大长度
|
119
|
-
|
120
|
-
>>> len_in_dim2([[1,1], [2], [3,3,3]])
|
121
|
-
3
|
122
|
-
|
123
|
-
>>> len_in_dim2([1, 2, 3]) # TODO 是不是应该改成0合理?但不知道牵涉到哪些功能影响
|
124
|
-
1
|
125
|
-
"""
|
126
|
-
if not isinstance(arr, (list, tuple)):
|
127
|
-
raise TypeError('类型错误,不是list构成的二维数组')
|
128
|
-
|
129
|
-
# 找出元素最多的列
|
130
|
-
column_num = 0
|
131
|
-
for i, item in enumerate(arr):
|
132
|
-
if isinstance(item, (list, tuple)): # 该行是一个一维数组
|
133
|
-
column_num = max(column_num, len(item))
|
134
|
-
else: # 如果不是数组,是指单个元素,当成1列处理
|
135
|
-
column_num = max(column_num, 1)
|
136
|
-
|
137
|
-
return column_num
|
138
|
-
|
139
|
-
|
140
|
-
def ensure_array(arr, default_value=''):
|
141
|
-
"""对一个由list、tuple组成的二维数组,确保所有第二维的列数都相同
|
142
|
-
|
143
|
-
>>> ensure_array([[1,1], [2], [3,3,3]])
|
144
|
-
[[1, 1, ''], [2, '', ''], [3, 3, 3]]
|
145
|
-
"""
|
146
|
-
max_cols = len_in_dim2(arr)
|
147
|
-
if max_cols == 1:
|
148
|
-
return arr
|
149
|
-
dv = str(default_value)
|
150
|
-
a = [[]] * len(arr)
|
151
|
-
for i, ls in enumerate(arr):
|
152
|
-
if isinstance(ls, (list, tuple)):
|
153
|
-
t = list(arr[i])
|
154
|
-
else:
|
155
|
-
t = [ls] # 如果不是数组,是指单个元素,当成1列处理
|
156
|
-
a[i] = t + [dv] * (max_cols - len(t)) # 左边的写list,是防止有的情况是tuple,要强制转list后拼接
|
157
|
-
return a
|
158
|
-
|
159
|
-
|
160
|
-
def swap_rowcol(a, *, ensure_arr=False, default_value=''):
|
161
|
-
"""矩阵行列互换
|
162
|
-
|
163
|
-
注:如果列数是不均匀的,则会以最小列数作为行数
|
164
|
-
|
165
|
-
>>> swap_rowcol([[1,2,3], [4,5,6]])
|
166
|
-
[[1, 4], [2, 5], [3, 6]]
|
167
|
-
"""
|
168
|
-
if ensure_arr:
|
169
|
-
a = ensure_array(a, default_value)
|
170
|
-
# 这是非常有教学意义的行列互换实现代码
|
171
|
-
return list(map(list, zip(*a)))
|
172
|
-
|
173
|
-
|
174
|
-
class GrowingList(list):
|
175
|
-
"""可变长list"""
|
176
|
-
|
177
|
-
def __init__(self, default_value=None):
|
178
|
-
super().__init__(self)
|
179
|
-
self.default_value = default_value
|
180
|
-
|
181
|
-
def __getitem__(self, index):
|
182
|
-
if index >= len(self):
|
183
|
-
self.extend([self.default_value] * (index + 1 - len(self)))
|
184
|
-
return list.__getitem__(self, index)
|
185
|
-
|
186
|
-
def __setitem__(self, index, value):
|
187
|
-
if index >= len(self):
|
188
|
-
self.extend([self.default_value] * (index + 1 - len(self)))
|
189
|
-
list.__setitem__(self, index, value)
|
190
|
-
|
191
|
-
|
192
|
-
class GenFunction:
|
193
|
-
""" 一般用来生成高阶函数的函数对象
|
194
|
-
|
195
|
-
这个名字可能还不是很精确,后面有想法再改
|
196
|
-
"""
|
197
|
-
|
198
|
-
@classmethod
|
199
|
-
def ensure_func(cls, x, default):
|
200
|
-
""" 确保x是callable对象,否则用default初始化 """
|
201
|
-
if callable(x):
|
202
|
-
return x
|
203
|
-
else:
|
204
|
-
return default
|
205
|
-
|
206
|
-
|
207
|
-
def first_nonnone(args, judge=None):
|
208
|
-
""" 返回第1个满足条件的值
|
209
|
-
|
210
|
-
:param args: 参数清单
|
211
|
-
:param judge: 判断器,默认返回第一个非None值,也可以自定义判定函数
|
212
|
-
"""
|
213
|
-
judge = GenFunction.ensure_func(judge, lambda x: x is not None)
|
214
|
-
for x in args:
|
215
|
-
if judge(x):
|
216
|
-
return x
|
217
|
-
return args[-1] # 全部都不满足,返回最后一个值
|
218
|
-
|
219
|
-
|
220
|
-
def round_int(x, *, ndim=0):
|
221
|
-
""" 先四舍五入,再取整
|
222
|
-
|
223
|
-
:param x: 一个数值,或者多维数组
|
224
|
-
:param ndim: x是数值是默认0,否则指定数组维度,批量处理
|
225
|
-
比如ndim=1是一维数组
|
226
|
-
ndim=2是二维数组
|
227
|
-
|
228
|
-
>>> round_int(1.5)
|
229
|
-
2
|
230
|
-
>>> round_int(1.4)
|
231
|
-
1
|
232
|
-
>>> round_int([2.3, 1.42], ndim=1)
|
233
|
-
[2, 1]
|
234
|
-
>>> round_int([[2.3, 1.42], [3.6]], ndim=2)
|
235
|
-
[[2, 1], [4]]
|
236
|
-
"""
|
237
|
-
if ndim:
|
238
|
-
return [round_int(a, ndim=ndim - 1) for a in x]
|
239
|
-
else:
|
240
|
-
return int(round(x, 0))
|
241
|
-
|
242
|
-
|
243
|
-
def human_readable_size(n):
|
244
|
-
""" 我个人习惯常用的size显示方式 """
|
245
|
-
for u in [' B', 'KB', 'MB', 'GB']:
|
246
|
-
if n < 1024:
|
247
|
-
return f'{round_int(n)}{u}'
|
248
|
-
else:
|
249
|
-
n /= 1024
|
250
|
-
else:
|
251
|
-
return f'{round_int(n)}TB'
|
252
|
-
|
253
|
-
|
254
|
-
def human_readable_number(value, base_type='K', precision=4):
|
255
|
-
""" 数字美化输出函数
|
256
|
-
|
257
|
-
:param float|int value: 要转换的数值
|
258
|
-
:param int precision: 有效数字的长度
|
259
|
-
:param str base_type: 进制类型,'K'为1000进制, 'KB'为1024进制(KiB同理), '万'为中文万进制
|
260
|
-
:return: 美化后的字符串
|
261
|
-
"""
|
262
|
-
if value is None:
|
263
|
-
return ''
|
264
|
-
|
265
|
-
if abs(value) < 1:
|
266
|
-
return f'{value:.{precision}g}'
|
267
|
-
|
268
|
-
# 设置不同进制的单位和基数
|
269
|
-
units, base = {
|
270
|
-
'K': (['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 1000),
|
271
|
-
'KB': (['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024),
|
272
|
-
'KiB': (['', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'], 1024),
|
273
|
-
'万': (['', '万', '亿', '万亿', '亿亿'], 10000),
|
274
|
-
'秒': (['秒', [60, '分'], [60, '时'], [24, '天'], [7, '周'], [4.345, '月'], [12, '年']], 60),
|
275
|
-
}.get(base_type, ([''], 1)) # 默认为空单位和基数1
|
276
|
-
|
277
|
-
x, i = abs(value), 0
|
278
|
-
while x >= base and i < len(units) - 1:
|
279
|
-
x /= base
|
280
|
-
i += 1
|
281
|
-
if isinstance(units[i], list):
|
282
|
-
base = units[i][0]
|
283
|
-
units[i] = units[i][1]
|
284
|
-
|
285
|
-
x = f'{x:.{precision}g}' # 四舍五入到指定精度
|
286
|
-
prefix = '-' if value < 0 else '' # 负数处理
|
287
|
-
return f"{prefix}{x}{units[i]}"
|
288
|
-
|
289
|
-
|
290
|
-
class CvtType:
|
291
|
-
""" 这些系列的转换函数,转换失败统一报错为ValueError
|
292
|
-
|
293
|
-
返回异常会比较好,否则返回None、False之类的,
|
294
|
-
有时候可能就是要转换的值呢,会有歧义
|
295
|
-
"""
|
296
|
-
|
297
|
-
@classmethod
|
298
|
-
def str2list(cls, arg):
|
299
|
-
try:
|
300
|
-
res = eval(arg)
|
301
|
-
except SyntaxError:
|
302
|
-
raise ValueError
|
303
|
-
|
304
|
-
if not isinstance(res, list):
|
305
|
-
raise ValueError
|
306
|
-
|
307
|
-
return res
|
308
|
-
|
309
|
-
@classmethod
|
310
|
-
def str2dict(cls, arg):
|
311
|
-
try:
|
312
|
-
res = eval(arg)
|
313
|
-
except SyntaxError:
|
314
|
-
raise ValueError
|
315
|
-
|
316
|
-
if not isinstance(res, dict):
|
317
|
-
raise ValueError
|
318
|
-
|
319
|
-
return res
|
320
|
-
|
321
|
-
@classmethod
|
322
|
-
def factory(cls, name):
|
323
|
-
""" 从字符串名称,映射到对应的转换函数 """
|
324
|
-
return {'int': int,
|
325
|
-
'float': float,
|
326
|
-
'str': str,
|
327
|
-
'list': cls.str2list,
|
328
|
-
'dict': cls.str2dict}.get(name, None)
|
329
|
-
|
330
|
-
|
331
|
-
def mod_minabs(x, m):
|
332
|
-
a = x % m
|
333
|
-
return a if a < m / 2 else a - m
|
334
|
-
|
335
|
-
|
336
|
-
class classproperty(property):
|
337
|
-
""" python - Using property() on classmethods - Stack Overflow
|
338
|
-
https://stackoverflow.com/questions/128573/using-property-on-classmethods
|
339
|
-
"""
|
340
|
-
|
341
|
-
def __get__(self, obj, objtype=None):
|
342
|
-
return super(classproperty, self).__get__(objtype)
|
343
|
-
|
344
|
-
def __set__(self, obj, value):
|
345
|
-
super(classproperty, self).__set__(type(obj), value)
|
346
|
-
|
347
|
-
def __delete__(self, obj):
|
348
|
-
super(classproperty, self).__delete__(type(obj))
|
349
|
-
|
350
|
-
|
351
|
-
def decode_bitflags(n, flags, return_type=dict):
|
352
|
-
""" 解析一个位标记的功能
|
353
|
-
|
354
|
-
:param int n: 一个整数标记
|
355
|
-
:param list|tuple flags: 一个对应明文数组
|
356
|
-
flags[0]对应2**0的明文,flags[1]对应2**1的明文,以此类推
|
357
|
-
:param type return_type: 返回的数据类型
|
358
|
-
默认dict,记录所有key的bool结果
|
359
|
-
可以设set,只返回真的标记结果
|
360
|
-
|
361
|
-
>>> decode_bitflags(18, ('superscript', 'italic', 'serifed', 'monospaced', 'bold'))
|
362
|
-
{'superscript': 2, 'italic': 0, 'serifed': 0, 'monospaced': 16, 'bold': 0}
|
363
|
-
>>> decode_bitflags(18, ('superscript', 'italic', 'serifed', 'monospaced', 'bold'), set)
|
364
|
-
{'superscript', 'monospaced'}
|
365
|
-
"""
|
366
|
-
if return_type == dict:
|
367
|
-
return {x: n & (2 << i) for i, x in enumerate(flags)}
|
368
|
-
elif return_type == set:
|
369
|
-
return {x for i, x in enumerate(flags) if (n & (2 << i))}
|
370
|
-
else:
|
371
|
-
raise ValueError
|
372
|
-
|
373
|
-
|
374
|
-
def xl_format_g(x, p=3):
|
375
|
-
""" 普通format的g模式不太满足我的需求,做了点修改
|
376
|
-
|
377
|
-
注:g是比较方便的一种数值格式化方法,会比较智能地判断是否整数显示,或者普通显示、科学计数法显示
|
378
|
-
|
379
|
-
:param x: 数值x
|
380
|
-
"""
|
381
|
-
s = f'{x:g}'
|
382
|
-
if 'e' in s:
|
383
|
-
# 如果变成了科学计数法,明确保留3位有效数字
|
384
|
-
return '{:.{}g}'.format(x, p=3)
|
385
|
-
else:
|
386
|
-
# 否则返回默认的g格式
|
387
|
-
return s
|
388
|
-
|
389
|
-
|
390
|
-
class EmptyWith:
|
391
|
-
""" 空上下文类
|
392
|
-
"""
|
393
|
-
|
394
|
-
def __enter__(self):
|
395
|
-
pass
|
396
|
-
|
397
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
398
|
-
pass
|
399
|
-
|
400
|
-
|
401
|
-
def funcmsg(func):
|
402
|
-
"""输出函数func所在的文件、函数名、函数起始行"""
|
403
|
-
# showdir(func)
|
404
|
-
if not hasattr(func, '__name__'): # 没有__name__属性表示这很可能是一个装饰器去处理原函数了
|
405
|
-
if hasattr(func, 'func'): # 我的装饰器常用func成员存储原函数对象
|
406
|
-
func = func.func
|
407
|
-
else:
|
408
|
-
return f'装饰器:{type(func)},无法定位'
|
409
|
-
return f'函数名:{func.__name__},来自文件:{func.__code__.co_filename},所在行号={func.__code__.co_firstlineno}'
|
410
|
-
|
411
|
-
|
412
|
-
def set_global_var(name, value):
|
413
|
-
""" 设置某个全局变量,一般用在一些特殊的需要跨作用域进行调试的场景
|
414
|
-
切勿!切勿!切勿用于正式功能,否则会导致难以维护控制的功能代码
|
415
|
-
|
416
|
-
为了避免和某些关键的全局变量名冲突,这里的变量命令统一会加上pyxllib_的前缀
|
417
|
-
"""
|
418
|
-
g = globals()
|
419
|
-
name = f'pyxllib_{name}'
|
420
|
-
g[name] = value
|
421
|
-
|
422
|
-
|
423
|
-
def get_global_var(name, default_value=None):
|
424
|
-
""" 获取某个全局变量的值 """
|
425
|
-
g = globals()
|
426
|
-
name = f'pyxllib_{name}'
|
427
|
-
if name not in g:
|
428
|
-
g[name] = default_value
|
429
|
-
return g[name]
|
430
|
-
|
431
|
-
|
432
|
-
def convert_to_json_compatible(d, custom_converter=None):
|
433
|
-
""" 递归地将字典等类型转换为JSON兼容格式。对于非标准JSON类型,使用自定义转换器或默认转换为字符串。
|
434
|
-
|
435
|
-
:param d: 要转换的字典或列表。
|
436
|
-
:param custom_converter: 自定义转换函数,用于处理非标准JSON类型的值。
|
437
|
-
:return: 转换后的字典或列表。
|
438
|
-
|
439
|
-
todo 这个函数想法是好的,但总感觉精确性中,总容易有些问题的,需要更多的考察测试
|
440
|
-
"""
|
441
|
-
if custom_converter is None:
|
442
|
-
custom_converter = str
|
443
|
-
|
444
|
-
if isinstance(d, dict): # defaultdict呢?
|
445
|
-
return {k: convert_to_json_compatible(v, custom_converter) for k, v in d.items()}
|
446
|
-
elif isinstance(d, list):
|
447
|
-
return [convert_to_json_compatible(v, custom_converter) for v in d]
|
448
|
-
elif isinstance(d, (int, float, str, bool)) or d is None:
|
449
|
-
return d
|
450
|
-
else:
|
451
|
-
return custom_converter(d)
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Author : 陈坤泽
|
4
|
+
# @Email : 877362867@qq.com
|
5
|
+
# @Date : 2021/06/06 10:51
|
6
|
+
|
7
|
+
|
8
|
+
def typename(c):
|
9
|
+
""" 简化输出的type类型
|
10
|
+
|
11
|
+
>>> typename(123)
|
12
|
+
'int'
|
13
|
+
"""
|
14
|
+
return str(type(c))[8:-2]
|
15
|
+
|
16
|
+
|
17
|
+
class SingletonForEveryClass(type):
|
18
|
+
""" 注意如果A是单例类,B从A继承,那么实际有且仅有A、B两个不同的实例对象 """
|
19
|
+
_instances = {}
|
20
|
+
|
21
|
+
def __call__(cls, *args, **kwargs):
|
22
|
+
tag = f'{cls}'
|
23
|
+
# 其实转字符串来判断是不太严谨的,有些类型字符串后的显示效果是一模一样的
|
24
|
+
# dprint(tag)
|
25
|
+
if tag not in cls._instances:
|
26
|
+
cls._instances[tag] = super(SingletonForEveryClass, cls).__call__(*args, **kwargs)
|
27
|
+
return cls._instances[tag]
|
28
|
+
|
29
|
+
|
30
|
+
class SingletonForEveryInitArgs(type):
|
31
|
+
""" Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
|
32
|
+
|
33
|
+
注意!注意!注意!重要的事说三遍!
|
34
|
+
这里的单例类不是传统意义上的单例类。
|
35
|
+
传统意义的单例类,不管用怎样不同的初始化参数创建对象,永远都只有最初的那个对象类。
|
36
|
+
但是这个单例类,为每种不同的参数创建形式,都构造了一个对象。
|
37
|
+
"""
|
38
|
+
_instances = {}
|
39
|
+
|
40
|
+
def __call__(cls, *args, **kwargs):
|
41
|
+
tag = f'{cls}{args}{kwargs}' # id加上所有参数的影响来控制单例类
|
42
|
+
# 其实转字符串来判断是不太严谨的,有些类型字符串后的显示效果是一模一样的
|
43
|
+
# dprint(tag)
|
44
|
+
if tag not in cls._instances:
|
45
|
+
cls._instances[tag] = super(SingletonForEveryInitArgs, cls).__call__(*args, **kwargs)
|
46
|
+
return cls._instances[tag]
|
47
|
+
|
48
|
+
|
49
|
+
def xlbool(v):
|
50
|
+
""" 有些类型不能直接判断真假,例如具有多值的np.array,df等
|
51
|
+
|
52
|
+
这些有歧义的情况,在我的mybool里暂时都判断为True,如果有需要,需要精细化判断,可以扩展自己的npbool、dfbool
|
53
|
+
"""
|
54
|
+
try:
|
55
|
+
return bool(v)
|
56
|
+
except ValueError:
|
57
|
+
return True
|
58
|
+
|
59
|
+
|
60
|
+
def document(func):
|
61
|
+
"""文档函数装饰器
|
62
|
+
用该装饰器器时,表明一个函数是用伪代码在表示一系列的操作逻辑,不能直接拿来执行的
|
63
|
+
很可能是一套半自动化工具
|
64
|
+
"""
|
65
|
+
|
66
|
+
def wrapper(*args):
|
67
|
+
raise RuntimeError(f'函数:{func.__name__} 是一个伪代码流程示例文档,不能直接运行')
|
68
|
+
|
69
|
+
return wrapper
|
70
|
+
|
71
|
+
|
72
|
+
class RunOnlyOnce:
|
73
|
+
""" 被装饰的函数,不同的参数输入形式,只会被执行一次,
|
74
|
+
|
75
|
+
重复执行时会从内存直接调用上次相同参数调用下的运行的结果
|
76
|
+
可以使用reset成员函数重置,下一次调用该函数时则会重新执行
|
77
|
+
|
78
|
+
文档:https://www.yuque.com/xlpr/pyxllib/RunOnlyOnce
|
79
|
+
|
80
|
+
使用好该装饰器,可能让一些动态规划dp、搜索问题变得更简洁,
|
81
|
+
以及一些配置文件操作,可以做到只读一遍
|
82
|
+
"""
|
83
|
+
|
84
|
+
def __init__(self, func, distinct_args=True):
|
85
|
+
"""
|
86
|
+
:param func: 封装的函数
|
87
|
+
:param distinct_args: 默认不同输入参数形式,都会保存一个结果
|
88
|
+
设为False,则不管何种参数形式,函数就真的只会保存第一次运行的结果
|
89
|
+
"""
|
90
|
+
self.func = func
|
91
|
+
self.distinct_args = distinct_args
|
92
|
+
self.results = {}
|
93
|
+
|
94
|
+
@classmethod
|
95
|
+
def decorator(cls, distinct_args=True):
|
96
|
+
""" 作为装饰器的时候,如果要设置参数,要用这个接口 """
|
97
|
+
|
98
|
+
def wrap(func):
|
99
|
+
return cls(func, distinct_args)
|
100
|
+
|
101
|
+
return wrap
|
102
|
+
|
103
|
+
def __call__(self, *args, **kwargs):
|
104
|
+
tag = f'{args}{kwargs}' if self.distinct_args else ''
|
105
|
+
# TODO 思考更严谨,考虑了值类型的tag标记
|
106
|
+
# 目前的tag规则,是必要不充分条件。还可以使用id,则是充分不必要条件
|
107
|
+
# 能找到充要条件来做是最好的,不行的话,也应该用更严谨的充分条件来做
|
108
|
+
# TODO kwargs的顺序应该是没影响的,要去掉顺序干扰
|
109
|
+
if tag not in self.results:
|
110
|
+
self.results[tag] = self.func(*args, **kwargs)
|
111
|
+
return self.results[tag]
|
112
|
+
|
113
|
+
def reset(self):
|
114
|
+
self.results = {}
|
115
|
+
|
116
|
+
|
117
|
+
def len_in_dim2(arr):
|
118
|
+
""" 计算类List结构在第2维上的最大长度
|
119
|
+
|
120
|
+
>>> len_in_dim2([[1,1], [2], [3,3,3]])
|
121
|
+
3
|
122
|
+
|
123
|
+
>>> len_in_dim2([1, 2, 3]) # TODO 是不是应该改成0合理?但不知道牵涉到哪些功能影响
|
124
|
+
1
|
125
|
+
"""
|
126
|
+
if not isinstance(arr, (list, tuple)):
|
127
|
+
raise TypeError('类型错误,不是list构成的二维数组')
|
128
|
+
|
129
|
+
# 找出元素最多的列
|
130
|
+
column_num = 0
|
131
|
+
for i, item in enumerate(arr):
|
132
|
+
if isinstance(item, (list, tuple)): # 该行是一个一维数组
|
133
|
+
column_num = max(column_num, len(item))
|
134
|
+
else: # 如果不是数组,是指单个元素,当成1列处理
|
135
|
+
column_num = max(column_num, 1)
|
136
|
+
|
137
|
+
return column_num
|
138
|
+
|
139
|
+
|
140
|
+
def ensure_array(arr, default_value=''):
|
141
|
+
"""对一个由list、tuple组成的二维数组,确保所有第二维的列数都相同
|
142
|
+
|
143
|
+
>>> ensure_array([[1,1], [2], [3,3,3]])
|
144
|
+
[[1, 1, ''], [2, '', ''], [3, 3, 3]]
|
145
|
+
"""
|
146
|
+
max_cols = len_in_dim2(arr)
|
147
|
+
if max_cols == 1:
|
148
|
+
return arr
|
149
|
+
dv = str(default_value)
|
150
|
+
a = [[]] * len(arr)
|
151
|
+
for i, ls in enumerate(arr):
|
152
|
+
if isinstance(ls, (list, tuple)):
|
153
|
+
t = list(arr[i])
|
154
|
+
else:
|
155
|
+
t = [ls] # 如果不是数组,是指单个元素,当成1列处理
|
156
|
+
a[i] = t + [dv] * (max_cols - len(t)) # 左边的写list,是防止有的情况是tuple,要强制转list后拼接
|
157
|
+
return a
|
158
|
+
|
159
|
+
|
160
|
+
def swap_rowcol(a, *, ensure_arr=False, default_value=''):
|
161
|
+
"""矩阵行列互换
|
162
|
+
|
163
|
+
注:如果列数是不均匀的,则会以最小列数作为行数
|
164
|
+
|
165
|
+
>>> swap_rowcol([[1,2,3], [4,5,6]])
|
166
|
+
[[1, 4], [2, 5], [3, 6]]
|
167
|
+
"""
|
168
|
+
if ensure_arr:
|
169
|
+
a = ensure_array(a, default_value)
|
170
|
+
# 这是非常有教学意义的行列互换实现代码
|
171
|
+
return list(map(list, zip(*a)))
|
172
|
+
|
173
|
+
|
174
|
+
class GrowingList(list):
|
175
|
+
"""可变长list"""
|
176
|
+
|
177
|
+
def __init__(self, default_value=None):
|
178
|
+
super().__init__(self)
|
179
|
+
self.default_value = default_value
|
180
|
+
|
181
|
+
def __getitem__(self, index):
|
182
|
+
if index >= len(self):
|
183
|
+
self.extend([self.default_value] * (index + 1 - len(self)))
|
184
|
+
return list.__getitem__(self, index)
|
185
|
+
|
186
|
+
def __setitem__(self, index, value):
|
187
|
+
if index >= len(self):
|
188
|
+
self.extend([self.default_value] * (index + 1 - len(self)))
|
189
|
+
list.__setitem__(self, index, value)
|
190
|
+
|
191
|
+
|
192
|
+
class GenFunction:
|
193
|
+
""" 一般用来生成高阶函数的函数对象
|
194
|
+
|
195
|
+
这个名字可能还不是很精确,后面有想法再改
|
196
|
+
"""
|
197
|
+
|
198
|
+
@classmethod
|
199
|
+
def ensure_func(cls, x, default):
|
200
|
+
""" 确保x是callable对象,否则用default初始化 """
|
201
|
+
if callable(x):
|
202
|
+
return x
|
203
|
+
else:
|
204
|
+
return default
|
205
|
+
|
206
|
+
|
207
|
+
def first_nonnone(args, judge=None):
|
208
|
+
""" 返回第1个满足条件的值
|
209
|
+
|
210
|
+
:param args: 参数清单
|
211
|
+
:param judge: 判断器,默认返回第一个非None值,也可以自定义判定函数
|
212
|
+
"""
|
213
|
+
judge = GenFunction.ensure_func(judge, lambda x: x is not None)
|
214
|
+
for x in args:
|
215
|
+
if judge(x):
|
216
|
+
return x
|
217
|
+
return args[-1] # 全部都不满足,返回最后一个值
|
218
|
+
|
219
|
+
|
220
|
+
def round_int(x, *, ndim=0):
|
221
|
+
""" 先四舍五入,再取整
|
222
|
+
|
223
|
+
:param x: 一个数值,或者多维数组
|
224
|
+
:param ndim: x是数值是默认0,否则指定数组维度,批量处理
|
225
|
+
比如ndim=1是一维数组
|
226
|
+
ndim=2是二维数组
|
227
|
+
|
228
|
+
>>> round_int(1.5)
|
229
|
+
2
|
230
|
+
>>> round_int(1.4)
|
231
|
+
1
|
232
|
+
>>> round_int([2.3, 1.42], ndim=1)
|
233
|
+
[2, 1]
|
234
|
+
>>> round_int([[2.3, 1.42], [3.6]], ndim=2)
|
235
|
+
[[2, 1], [4]]
|
236
|
+
"""
|
237
|
+
if ndim:
|
238
|
+
return [round_int(a, ndim=ndim - 1) for a in x]
|
239
|
+
else:
|
240
|
+
return int(round(x, 0))
|
241
|
+
|
242
|
+
|
243
|
+
def human_readable_size(n):
|
244
|
+
""" 我个人习惯常用的size显示方式 """
|
245
|
+
for u in [' B', 'KB', 'MB', 'GB']:
|
246
|
+
if n < 1024:
|
247
|
+
return f'{round_int(n)}{u}'
|
248
|
+
else:
|
249
|
+
n /= 1024
|
250
|
+
else:
|
251
|
+
return f'{round_int(n)}TB'
|
252
|
+
|
253
|
+
|
254
|
+
def human_readable_number(value, base_type='K', precision=4):
|
255
|
+
""" 数字美化输出函数
|
256
|
+
|
257
|
+
:param float|int value: 要转换的数值
|
258
|
+
:param int precision: 有效数字的长度
|
259
|
+
:param str base_type: 进制类型,'K'为1000进制, 'KB'为1024进制(KiB同理), '万'为中文万进制
|
260
|
+
:return: 美化后的字符串
|
261
|
+
"""
|
262
|
+
if value is None:
|
263
|
+
return ''
|
264
|
+
|
265
|
+
if abs(value) < 1:
|
266
|
+
return f'{value:.{precision}g}'
|
267
|
+
|
268
|
+
# 设置不同进制的单位和基数
|
269
|
+
units, base = {
|
270
|
+
'K': (['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 1000),
|
271
|
+
'KB': (['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024),
|
272
|
+
'KiB': (['', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'], 1024),
|
273
|
+
'万': (['', '万', '亿', '万亿', '亿亿'], 10000),
|
274
|
+
'秒': (['秒', [60, '分'], [60, '时'], [24, '天'], [7, '周'], [4.345, '月'], [12, '年']], 60),
|
275
|
+
}.get(base_type, ([''], 1)) # 默认为空单位和基数1
|
276
|
+
|
277
|
+
x, i = abs(value), 0
|
278
|
+
while x >= base and i < len(units) - 1:
|
279
|
+
x /= base
|
280
|
+
i += 1
|
281
|
+
if isinstance(units[i], list):
|
282
|
+
base = units[i][0]
|
283
|
+
units[i] = units[i][1]
|
284
|
+
|
285
|
+
x = f'{x:.{precision}g}' # 四舍五入到指定精度
|
286
|
+
prefix = '-' if value < 0 else '' # 负数处理
|
287
|
+
return f"{prefix}{x}{units[i]}"
|
288
|
+
|
289
|
+
|
290
|
+
class CvtType:
|
291
|
+
""" 这些系列的转换函数,转换失败统一报错为ValueError
|
292
|
+
|
293
|
+
返回异常会比较好,否则返回None、False之类的,
|
294
|
+
有时候可能就是要转换的值呢,会有歧义
|
295
|
+
"""
|
296
|
+
|
297
|
+
@classmethod
|
298
|
+
def str2list(cls, arg):
|
299
|
+
try:
|
300
|
+
res = eval(arg)
|
301
|
+
except SyntaxError:
|
302
|
+
raise ValueError
|
303
|
+
|
304
|
+
if not isinstance(res, list):
|
305
|
+
raise ValueError
|
306
|
+
|
307
|
+
return res
|
308
|
+
|
309
|
+
@classmethod
|
310
|
+
def str2dict(cls, arg):
|
311
|
+
try:
|
312
|
+
res = eval(arg)
|
313
|
+
except SyntaxError:
|
314
|
+
raise ValueError
|
315
|
+
|
316
|
+
if not isinstance(res, dict):
|
317
|
+
raise ValueError
|
318
|
+
|
319
|
+
return res
|
320
|
+
|
321
|
+
@classmethod
|
322
|
+
def factory(cls, name):
|
323
|
+
""" 从字符串名称,映射到对应的转换函数 """
|
324
|
+
return {'int': int,
|
325
|
+
'float': float,
|
326
|
+
'str': str,
|
327
|
+
'list': cls.str2list,
|
328
|
+
'dict': cls.str2dict}.get(name, None)
|
329
|
+
|
330
|
+
|
331
|
+
def mod_minabs(x, m):
|
332
|
+
a = x % m
|
333
|
+
return a if a < m / 2 else a - m
|
334
|
+
|
335
|
+
|
336
|
+
class classproperty(property):
|
337
|
+
""" python - Using property() on classmethods - Stack Overflow
|
338
|
+
https://stackoverflow.com/questions/128573/using-property-on-classmethods
|
339
|
+
"""
|
340
|
+
|
341
|
+
def __get__(self, obj, objtype=None):
|
342
|
+
return super(classproperty, self).__get__(objtype)
|
343
|
+
|
344
|
+
def __set__(self, obj, value):
|
345
|
+
super(classproperty, self).__set__(type(obj), value)
|
346
|
+
|
347
|
+
def __delete__(self, obj):
|
348
|
+
super(classproperty, self).__delete__(type(obj))
|
349
|
+
|
350
|
+
|
351
|
+
def decode_bitflags(n, flags, return_type=dict):
|
352
|
+
""" 解析一个位标记的功能
|
353
|
+
|
354
|
+
:param int n: 一个整数标记
|
355
|
+
:param list|tuple flags: 一个对应明文数组
|
356
|
+
flags[0]对应2**0的明文,flags[1]对应2**1的明文,以此类推
|
357
|
+
:param type return_type: 返回的数据类型
|
358
|
+
默认dict,记录所有key的bool结果
|
359
|
+
可以设set,只返回真的标记结果
|
360
|
+
|
361
|
+
>>> decode_bitflags(18, ('superscript', 'italic', 'serifed', 'monospaced', 'bold'))
|
362
|
+
{'superscript': 2, 'italic': 0, 'serifed': 0, 'monospaced': 16, 'bold': 0}
|
363
|
+
>>> decode_bitflags(18, ('superscript', 'italic', 'serifed', 'monospaced', 'bold'), set)
|
364
|
+
{'superscript', 'monospaced'}
|
365
|
+
"""
|
366
|
+
if return_type == dict:
|
367
|
+
return {x: n & (2 << i) for i, x in enumerate(flags)}
|
368
|
+
elif return_type == set:
|
369
|
+
return {x for i, x in enumerate(flags) if (n & (2 << i))}
|
370
|
+
else:
|
371
|
+
raise ValueError
|
372
|
+
|
373
|
+
|
374
|
+
def xl_format_g(x, p=3):
|
375
|
+
""" 普通format的g模式不太满足我的需求,做了点修改
|
376
|
+
|
377
|
+
注:g是比较方便的一种数值格式化方法,会比较智能地判断是否整数显示,或者普通显示、科学计数法显示
|
378
|
+
|
379
|
+
:param x: 数值x
|
380
|
+
"""
|
381
|
+
s = f'{x:g}'
|
382
|
+
if 'e' in s:
|
383
|
+
# 如果变成了科学计数法,明确保留3位有效数字
|
384
|
+
return '{:.{}g}'.format(x, p=3)
|
385
|
+
else:
|
386
|
+
# 否则返回默认的g格式
|
387
|
+
return s
|
388
|
+
|
389
|
+
|
390
|
+
class EmptyWith:
|
391
|
+
""" 空上下文类
|
392
|
+
"""
|
393
|
+
|
394
|
+
def __enter__(self):
|
395
|
+
pass
|
396
|
+
|
397
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
398
|
+
pass
|
399
|
+
|
400
|
+
|
401
|
+
def funcmsg(func):
|
402
|
+
"""输出函数func所在的文件、函数名、函数起始行"""
|
403
|
+
# showdir(func)
|
404
|
+
if not hasattr(func, '__name__'): # 没有__name__属性表示这很可能是一个装饰器去处理原函数了
|
405
|
+
if hasattr(func, 'func'): # 我的装饰器常用func成员存储原函数对象
|
406
|
+
func = func.func
|
407
|
+
else:
|
408
|
+
return f'装饰器:{type(func)},无法定位'
|
409
|
+
return f'函数名:{func.__name__},来自文件:{func.__code__.co_filename},所在行号={func.__code__.co_firstlineno}'
|
410
|
+
|
411
|
+
|
412
|
+
def set_global_var(name, value):
|
413
|
+
""" 设置某个全局变量,一般用在一些特殊的需要跨作用域进行调试的场景
|
414
|
+
切勿!切勿!切勿用于正式功能,否则会导致难以维护控制的功能代码
|
415
|
+
|
416
|
+
为了避免和某些关键的全局变量名冲突,这里的变量命令统一会加上pyxllib_的前缀
|
417
|
+
"""
|
418
|
+
g = globals()
|
419
|
+
name = f'pyxllib_{name}'
|
420
|
+
g[name] = value
|
421
|
+
|
422
|
+
|
423
|
+
def get_global_var(name, default_value=None):
|
424
|
+
""" 获取某个全局变量的值 """
|
425
|
+
g = globals()
|
426
|
+
name = f'pyxllib_{name}'
|
427
|
+
if name not in g:
|
428
|
+
g[name] = default_value
|
429
|
+
return g[name]
|
430
|
+
|
431
|
+
|
432
|
+
def convert_to_json_compatible(d, custom_converter=None):
|
433
|
+
""" 递归地将字典等类型转换为JSON兼容格式。对于非标准JSON类型,使用自定义转换器或默认转换为字符串。
|
434
|
+
|
435
|
+
:param d: 要转换的字典或列表。
|
436
|
+
:param custom_converter: 自定义转换函数,用于处理非标准JSON类型的值。
|
437
|
+
:return: 转换后的字典或列表。
|
438
|
+
|
439
|
+
todo 这个函数想法是好的,但总感觉精确性中,总容易有些问题的,需要更多的考察测试
|
440
|
+
"""
|
441
|
+
if custom_converter is None:
|
442
|
+
custom_converter = str
|
443
|
+
|
444
|
+
if isinstance(d, dict): # defaultdict呢?
|
445
|
+
return {k: convert_to_json_compatible(v, custom_converter) for k, v in d.items()}
|
446
|
+
elif isinstance(d, list):
|
447
|
+
return [convert_to_json_compatible(v, custom_converter) for v in d]
|
448
|
+
elif isinstance(d, (int, float, str, bool)) or d is None:
|
449
|
+
return d
|
450
|
+
else:
|
451
|
+
return custom_converter(d)
|