pyxllib 0.0.43__py3-none-any.whl → 0.3.197__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.
Files changed (186) hide show
  1. pyxllib/__init__.py +9 -2
  2. pyxllib/algo/__init__.py +8 -0
  3. pyxllib/algo/disjoint.py +54 -0
  4. pyxllib/algo/geo.py +541 -0
  5. pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
  6. pyxllib/algo/matcher.py +389 -0
  7. pyxllib/algo/newbie.py +166 -0
  8. pyxllib/algo/pupil.py +629 -0
  9. pyxllib/algo/shapelylib.py +67 -0
  10. pyxllib/algo/specialist.py +241 -0
  11. pyxllib/algo/stat.py +494 -0
  12. pyxllib/algo/treelib.py +149 -0
  13. pyxllib/algo/unitlib.py +66 -0
  14. pyxllib/autogui/__init__.py +5 -0
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/autogui/autogui.py +852 -0
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/autogui/virtualkey.py +102 -0
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  23. pyxllib/cv/__init__.py +1 -11
  24. pyxllib/cv/expert.py +267 -0
  25. pyxllib/cv/{imlib.py → imfile.py} +18 -83
  26. pyxllib/cv/imhash.py +39 -0
  27. pyxllib/cv/pupil.py +9 -0
  28. pyxllib/cv/rgbfmt.py +1525 -0
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +163 -49
  31. pyxllib/cv/xlcvlib.py +1040 -0
  32. pyxllib/cv/xlpillib.py +423 -0
  33. pyxllib/data/__init__.py +0 -0
  34. pyxllib/data/echarts.py +240 -0
  35. pyxllib/data/jsonlib.py +89 -0
  36. pyxllib/{util/oss2_.py → data/oss.py} +11 -9
  37. pyxllib/data/pglib.py +1127 -0
  38. pyxllib/data/sqlite.py +568 -0
  39. pyxllib/{util → data}/sqllib.py +13 -31
  40. pyxllib/ext/JLineViewer.py +505 -0
  41. pyxllib/ext/__init__.py +6 -0
  42. pyxllib/{util → ext}/demolib.py +119 -35
  43. pyxllib/ext/drissionlib.py +277 -0
  44. pyxllib/ext/kq5034lib.py +12 -0
  45. pyxllib/{util/main.py → ext/old.py} +122 -284
  46. pyxllib/ext/qt.py +449 -0
  47. pyxllib/ext/robustprocfile.py +497 -0
  48. pyxllib/ext/seleniumlib.py +76 -0
  49. pyxllib/{util/tklib.py → ext/tk.py} +10 -11
  50. pyxllib/ext/unixlib.py +827 -0
  51. pyxllib/ext/utools.py +351 -0
  52. pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
  53. pyxllib/ext/win32lib.py +40 -0
  54. pyxllib/ext/wjxlib.py +88 -0
  55. pyxllib/ext/wpsapi.py +124 -0
  56. pyxllib/ext/xlwork.py +9 -0
  57. pyxllib/ext/yuquelib.py +1105 -0
  58. pyxllib/file/__init__.py +17 -0
  59. pyxllib/file/docxlib.py +761 -0
  60. pyxllib/{util → file}/gitlib.py +40 -27
  61. pyxllib/file/libreoffice.py +165 -0
  62. pyxllib/file/movielib.py +148 -0
  63. pyxllib/file/newbie.py +10 -0
  64. pyxllib/file/onenotelib.py +1469 -0
  65. pyxllib/file/packlib/__init__.py +330 -0
  66. pyxllib/{util → file/packlib}/zipfile.py +598 -195
  67. pyxllib/file/pdflib.py +426 -0
  68. pyxllib/file/pupil.py +185 -0
  69. pyxllib/file/specialist/__init__.py +685 -0
  70. pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
  71. pyxllib/file/specialist/download.py +193 -0
  72. pyxllib/file/specialist/filelib.py +2829 -0
  73. pyxllib/file/xlsxlib.py +3131 -0
  74. pyxllib/file/xlsyncfile.py +341 -0
  75. pyxllib/prog/__init__.py +5 -0
  76. pyxllib/prog/cachetools.py +64 -0
  77. pyxllib/prog/deprecatedlib.py +233 -0
  78. pyxllib/prog/filelock.py +42 -0
  79. pyxllib/prog/ipyexec.py +253 -0
  80. pyxllib/prog/multiprogs.py +940 -0
  81. pyxllib/prog/newbie.py +451 -0
  82. pyxllib/prog/pupil.py +1197 -0
  83. pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
  84. pyxllib/prog/specialist/__init__.py +391 -0
  85. pyxllib/prog/specialist/bc.py +203 -0
  86. pyxllib/prog/specialist/browser.py +497 -0
  87. pyxllib/prog/specialist/common.py +347 -0
  88. pyxllib/prog/specialist/datetime.py +199 -0
  89. pyxllib/prog/specialist/tictoc.py +240 -0
  90. pyxllib/prog/specialist/xllog.py +180 -0
  91. pyxllib/prog/xlosenv.py +108 -0
  92. pyxllib/stdlib/__init__.py +17 -0
  93. pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
  94. pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
  95. pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
  96. pyxllib/text/__init__.py +8 -0
  97. pyxllib/text/ahocorasick.py +39 -0
  98. pyxllib/text/airscript.js +744 -0
  99. pyxllib/text/charclasslib.py +121 -0
  100. pyxllib/text/jiebalib.py +267 -0
  101. pyxllib/text/jinjalib.py +32 -0
  102. pyxllib/text/jsa_ai_prompt.md +271 -0
  103. pyxllib/text/jscode.py +922 -0
  104. pyxllib/text/latex/__init__.py +158 -0
  105. pyxllib/text/levenshtein.py +303 -0
  106. pyxllib/text/nestenv.py +1215 -0
  107. pyxllib/text/newbie.py +300 -0
  108. pyxllib/text/pupil/__init__.py +8 -0
  109. pyxllib/text/pupil/common.py +1121 -0
  110. pyxllib/text/pupil/xlalign.py +326 -0
  111. pyxllib/text/pycode.py +47 -0
  112. pyxllib/text/specialist/__init__.py +8 -0
  113. pyxllib/text/specialist/common.py +112 -0
  114. pyxllib/text/specialist/ptag.py +186 -0
  115. pyxllib/text/spellchecker.py +172 -0
  116. pyxllib/text/templates/echart_base.html +11 -0
  117. pyxllib/text/templates/highlight_code.html +17 -0
  118. pyxllib/text/templates/latex_editor.html +103 -0
  119. pyxllib/text/vbacode.py +17 -0
  120. pyxllib/text/xmllib.py +747 -0
  121. pyxllib/xl.py +39 -0
  122. pyxllib/xlcv.py +17 -0
  123. pyxllib-0.3.197.dist-info/METADATA +48 -0
  124. pyxllib-0.3.197.dist-info/RECORD +126 -0
  125. {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
  126. pyxllib/basic/_1_strlib.py +0 -945
  127. pyxllib/basic/_2_timelib.py +0 -488
  128. pyxllib/basic/_3_pathlib.py +0 -916
  129. pyxllib/basic/_4_loglib.py +0 -419
  130. pyxllib/basic/__init__.py +0 -54
  131. pyxllib/basic/arrow_.py +0 -250
  132. pyxllib/basic/chardet_.py +0 -66
  133. pyxllib/basic/dirlib.py +0 -529
  134. pyxllib/basic/dprint.py +0 -202
  135. pyxllib/basic/extension.py +0 -12
  136. pyxllib/basic/judge.py +0 -31
  137. pyxllib/basic/log.py +0 -204
  138. pyxllib/basic/pathlib_.py +0 -705
  139. pyxllib/basic/pytictoc.py +0 -102
  140. pyxllib/basic/qiniu_.py +0 -61
  141. pyxllib/basic/strlib.py +0 -761
  142. pyxllib/basic/timer.py +0 -132
  143. pyxllib/cv/cv.py +0 -834
  144. pyxllib/cv/cvlib/_1_geo.py +0 -543
  145. pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
  146. pyxllib/cv/cvlib/_2_imgproc.py +0 -594
  147. pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
  148. pyxllib/cv/cvlib/_4_cvimg.py +0 -211
  149. pyxllib/cv/cvlib/__init__.py +0 -10
  150. pyxllib/cv/debugtools.py +0 -82
  151. pyxllib/cv/fitz_.py +0 -300
  152. pyxllib/cv/installer.py +0 -42
  153. pyxllib/debug/_0_installer.py +0 -38
  154. pyxllib/debug/_1_typelib.py +0 -277
  155. pyxllib/debug/_2_chrome.py +0 -198
  156. pyxllib/debug/_3_showdir.py +0 -161
  157. pyxllib/debug/_4_bcompare.py +0 -140
  158. pyxllib/debug/__init__.py +0 -49
  159. pyxllib/debug/bcompare.py +0 -132
  160. pyxllib/debug/chrome.py +0 -198
  161. pyxllib/debug/installer.py +0 -38
  162. pyxllib/debug/showdir.py +0 -158
  163. pyxllib/debug/typelib.py +0 -278
  164. pyxllib/image/__init__.py +0 -12
  165. pyxllib/torch/__init__.py +0 -20
  166. pyxllib/torch/modellib.py +0 -37
  167. pyxllib/torch/trainlib.py +0 -344
  168. pyxllib/util/__init__.py +0 -20
  169. pyxllib/util/aip_.py +0 -141
  170. pyxllib/util/casiadb.py +0 -59
  171. pyxllib/util/excellib.py +0 -495
  172. pyxllib/util/filelib.py +0 -612
  173. pyxllib/util/jsondata.py +0 -27
  174. pyxllib/util/jsondata2.py +0 -92
  175. pyxllib/util/labelmelib.py +0 -139
  176. pyxllib/util/onepy/__init__.py +0 -29
  177. pyxllib/util/onepy/onepy.py +0 -574
  178. pyxllib/util/onepy/onmanager.py +0 -170
  179. pyxllib/util/pyautogui_.py +0 -219
  180. pyxllib/util/textlib.py +0 -1305
  181. pyxllib/util/unorder.py +0 -22
  182. pyxllib/util/xmllib.py +0 -639
  183. pyxllib-0.0.43.dist-info/METADATA +0 -39
  184. pyxllib-0.0.43.dist-info/RECORD +0 -80
  185. pyxllib-0.0.43.dist-info/top_level.txt +0 -1
  186. {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
@@ -1,945 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Data : 2020/06/01
6
-
7
-
8
- """
9
- TODO 这个文件结构还有点乱,需要梳理
10
- """
11
-
12
- import copy
13
- import io
14
- import logging
15
- import math
16
- import pprint
17
- import re
18
- import socket
19
- import struct
20
- import sys
21
- import textwrap
22
-
23
- from disjoint_set import DisjointSet
24
-
25
- HOSTNAME = socket.getfqdn()
26
-
27
- ____str_funcs = """
28
- 字符串类的一些辅助函数
29
- """
30
-
31
-
32
- def shorten(s, width=200, placeholder='...'):
33
- """
34
- :param width: 这个长度是上限,即使用placeholder时的字符串总长度也在这个范围内
35
-
36
- >>> shorten('aaa', 10)
37
- 'aaa'
38
- >>> shorten('hell world! 0123456789 0123456789', 11)
39
- 'hell wor...'
40
- >>> shorten("Hello world!", width=12)
41
- 'Hello world!'
42
- >>> shorten("Hello world!", width=11)
43
- 'Hello wo...'
44
- >>> shorten('0123456789 0123456789', 2, 'xyz') # 自己写的shorten
45
- 'xy'
46
-
47
- 注意textwrap.shorten的缩略只针对空格隔开的单词有效,我这里的功能与其不太一样
48
- >>> textwrap.shorten('0123456789 0123456789', 11) # 全部字符都被折叠了
49
- '[...]'
50
- >>> shorten('0123456789 0123456789', 11) # 自己写的shorten
51
- '01234567...'
52
- """
53
- s = re.sub(r'\s+', ' ', str(s))
54
- n, m = len(s), len(placeholder)
55
- if n > width:
56
- s = s[:max(width - m, 0)] + placeholder
57
- return s[:width] # 加了placeholder在特殊情况下也会超,再做个截断最保险
58
-
59
- # return textwrap.shorten(str(s), width)
60
-
61
-
62
- def natural_sort_key(key):
63
- def convert(text):
64
- return int(text) if text.isdigit() else text.lower()
65
-
66
- return [convert(c) for c in re.split('([0-9]+)', str(key))]
67
-
68
-
69
- def natural_sort(ls, only_use_digits=False):
70
- """ 自然排序
71
-
72
- :param only_use_digits: 正常会用数字作为分隔,切割每一部分进行比较
73
- 如果只想比较数值部分,可以only_use_digits=True
74
-
75
- >>> natural_sort(['0.1.12', '0.0.10', '0.0.23'])
76
- ['0.0.10', '0.0.23', '0.1.12']
77
- """
78
- if only_use_digits:
79
- def func(key):
80
- return [int(c) for c in re.split('([0-9]+)', str(key)) if c.isdigit()]
81
- else:
82
- func = natural_sort_key
83
- return sorted(ls, key=func)
84
-
85
-
86
- def strwidth(s):
87
- """ string width
88
-
89
- 中英字符串实际宽度
90
- >>> strwidth('ab')
91
- 2
92
- >>> strwidth('a⑪中⑩')
93
- 7
94
-
95
- ⑩等字符的宽度还是跟字体有关的,不过在大部分地方好像都是域宽2,目前算法问题不大
96
- """
97
- try:
98
- res = len(s.encode('gbk'))
99
- except UnicodeEncodeError:
100
- count = len(s)
101
- for x in s:
102
- if ord(x) > 127:
103
- count += 1
104
- res = count
105
- return res
106
-
107
-
108
- def strwidth_proc(s, fmt='r', chinese_char_width=1.8):
109
- """ 此函数可以用于每个汉字域宽是w=1.8等奇怪的情况
110
-
111
- 为了让字符串域宽为一个整数,需要补充中文空格,会对原始字符串进行修改。
112
- 故返回值有2个,第1个是修正后的字符串s,第2个是实际宽度w。
113
-
114
- :param s: 一个字符串
115
- :param fmt: 目标对齐格式
116
- :param chinese_char_width: 每个汉字字符宽度
117
- :return: (s, w)
118
- s: 修正后的字符串值s
119
- w: 修正后字符串的实际宽度
120
-
121
- >>> strwidth_proc('哈哈a', chinese_char_width=1.8)
122
- ('   哈哈a', 10)
123
- """
124
- # 1 计算一些参数值
125
- s = str(s) # 确保是字符串类型
126
- l1 = len(s)
127
- l2 = strwidth(s)
128
- y = l2 - l1 # 中文字符数
129
- x = l1 - y # 英文字符数
130
- ch = chr(12288) # 中文空格
131
- w = x + y * chinese_char_width # 当前字符串宽度
132
- # 2 计算需要补充t个中文空格
133
- error = 0.05 # 允许误差范围
134
- t = 0 # 需要补充中文字符数
135
- while error < w % 1 < 1 - error: # 小数部分超过误差
136
- t += 1
137
- w += chinese_char_width
138
- # 3 补充中文字符
139
- if t:
140
- if fmt == 'r':
141
- s = ch * t + s
142
- elif fmt == 'l':
143
- s = s + ch * t
144
- else:
145
- s = ch * (t - t // 2) + s + ch * (t // 2)
146
- return s, int(w)
147
-
148
-
149
- def strfind(fullstr, objstr, *, start=None, times=0, overlap=False):
150
- r"""进行强大功能扩展的的字符串查找函数
151
-
152
- TODO 性能有待优化
153
-
154
- :param fullstr: 原始完整字符串
155
- >>> strfind('aabbaabb', 'bb') # 函数基本用法
156
- 2
157
-
158
- :param objstr: 需要查找的目标字符串,可以是一个list或tuple
159
- TODO 有空看下AC自动机,看这里是否可以优化提速,或者找现成的库接口
160
- >>> strfind('bbaaaabb', 'bb') # 查找第1次出现的位置
161
- 0
162
- >>> strfind('aabbaabb', 'bb', times=1) # 查找第2次出现的位置
163
- 6
164
- >>> strfind('aabbaabb', 'cc') # 不存在时返回-1
165
- -1
166
- >>> strfind('aabbaabb', ['aa', 'bb'], times=2)
167
- 4
168
-
169
- :param start: 起始查找位置。默认值为0,当times<0时start的默认值为-1。
170
- >>> strfind('aabbaabb', 'bb', start=2) # 恰好在起始位置
171
- 2
172
- >>> strfind('aabbaabb', 'bb', start=3)
173
- 6
174
- >>> strfind('aabbaabb', ['aa', 'bb'], start=5)
175
- 6
176
-
177
- :param times: 定位第几次出现的位置,默认值为0,即从前往后第1次出现的位置。
178
- 如果是负数,则反向查找,并返回的是目标字符串的起始位置。
179
- >>> strfind('aabbaabb', 'aa', times=-1)
180
- 4
181
- >>> strfind('aabbaabb', 'aa', start=5, times=-1)
182
- 4
183
- >>> strfind('aabbaabb', 'aa', start=3, times=-1)
184
- 0
185
- >>> strfind('aabbaabb', 'bb', start=7, times=-1)
186
- 6
187
-
188
- :param overlap: 重叠情况是否重复计数
189
- >>> strfind('aaaa', 'aa', times=1) # 默认不计算重叠部分
190
- 2
191
- >>> strfind('aaaa', 'aa', times=1, overlap=True)
192
- 1
193
-
194
- >>> strfind(r'\item=\item+', (r'\item', r'\test'), start=1)
195
- 6
196
- """
197
-
198
- def nonnegative_min_value(*arr):
199
- """计算出最小非负整数,如果没有非负数,则返回-1"""
200
- arr = tuple(filter(lambda x: x >= 0, arr))
201
- return min(arr) if arr else -1
202
-
203
- def nonnegative_max_value(*arr):
204
- """计算出最大非负整数,如果没有非负数,则返回-1"""
205
- arr = tuple(filter(lambda x: x >= 0, arr))
206
- return max(arr) if arr else -1
207
-
208
- # 1 根据times不同,start的初始默认值设置方式也不同
209
- if times < 0 and start is None:
210
- start = len(fullstr) - 1 # 反向查找start设到末尾字符-1
211
- if start is None:
212
- start = 0 # 正向查找start设为0
213
- p = -1 # 记录答案位置,默认找不到
214
-
215
- # 2 单串匹配
216
- if isinstance(objstr, str): # 单串匹配
217
- offset = 1 if overlap else len(objstr) # overlap影响每次偏移量
218
-
219
- # A、正向查找
220
- if times >= 0:
221
- p = start - offset
222
- for _ in range(times + 1):
223
- p = fullstr.find(objstr, p + offset)
224
- if p == -1:
225
- return -1
226
-
227
- # B、反向查找
228
- else:
229
- p = start + offset + 1
230
- for _ in range(-times):
231
- p = fullstr.rfind(objstr, 0, p - offset)
232
- if p == -1:
233
- return -1
234
-
235
- # 3 多模式匹配(递归调用,依赖单串匹配功能)
236
- else:
237
- # A、正向查找
238
- if times >= 0:
239
- p = start - 1
240
- for _ in range(times + 1):
241
- # 把每个目标串都找一遍下一次出现的位置,取最近的一个
242
- # 因为只找第一次出现的位置,所以overlap参数传不传都没有影响
243
- # TODO 需要进行性能对比分析,有必要的话后续可以改AC自动机实现多模式匹配
244
- ls = tuple(map(lambda x: strfind(fullstr, x, start=p + 1, overlap=overlap), objstr))
245
- p = nonnegative_min_value(*ls)
246
- if p == -1:
247
- return -1
248
-
249
- # B、反向查找
250
- else:
251
- p = start + 1
252
- for _ in range(-times): # 需要循环处理的次数
253
- # 使用map对每个要查找的目标调用strfind
254
- ls = tuple(map(lambda x: strfind(fullstr, x, start=p - 1, times=-1, overlap=overlap), objstr))
255
- p = nonnegative_max_value(*ls)
256
- if p == -1:
257
- return -1
258
-
259
- return p
260
-
261
-
262
- def typename(c):
263
- """ 简化输出的type类型
264
-
265
- >>> typename(123)
266
- 'int'
267
- """
268
- return str(type(c))[8:-2]
269
-
270
-
271
- ____str = """
272
- 文本处理相关功能
273
- """
274
-
275
-
276
- class StrDecorator:
277
- """将函数的返回值字符串化,仅调用朴素的str字符串化
278
-
279
- 装饰器开发可参考: https://mp.weixin.qq.com/s/Om98PpncG52Ba1ZQ8NIjLA
280
- """
281
-
282
- def __init__(self, func):
283
- self.func = func # 使用self.func可以索引回原始函数名称
284
- self.last_raw_res = None # last raw result,上一次执行函数的原始结果
285
-
286
- def __call__(self, *args, **kwargs):
287
- self.last_raw_res = self.func(*args, **kwargs)
288
- return str(self.last_raw_res)
289
-
290
-
291
- class PrintDecorator:
292
- """将函数返回结果直接输出"""
293
-
294
- def __init__(self, func):
295
- self.func = func
296
-
297
- def __call__(self, *args, **kwargs):
298
- s = self.func(*args, **kwargs)
299
- print(s)
300
- return s # 输出后仍然会返回原函数运行值
301
-
302
-
303
- def realign(text, least_blank=4, tab2blank=4, support_chinese=False, sep=None):
304
- r"""
305
- :param text: 一段文本
306
- 支持每行列数不同
307
- :param least_blank: 每列最少间距空格数
308
- :param tab2blank:
309
- :param support_chinese: 支持中文域宽计算
310
- :param sep: 每列分隔符,默认为least_blank个空格
311
- :return: 对齐美化的一段文本
312
-
313
- >>> realign(' Aget keep hold show\nmaking selling giving collecting')
314
- 'Aget keep hold show\nmaking selling giving collecting'
315
- """
316
- # 1 预处理
317
- s = text.replace('\t', ' ' * tab2blank)
318
- s = re.sub(' {' + str(least_blank) + ',}', r'\t', s) # 统一用\t作为分隔符
319
- lenfunc = strwidth if support_chinese else len
320
- if sep is None: sep = ' ' * least_blank
321
-
322
- # 2 计算出每一列的最大宽度
323
- lines = s.splitlines()
324
- n = len(lines)
325
- max_width = GrowingList() # 因为不知道有多少列,用自增长的list来存储每一列的最大宽度
326
- for i, line in enumerate(lines):
327
- line = line.strip().split('\t')
328
- m = len(line)
329
- for j in range(m): max_width[j] = max(max_width[j] or 0, lenfunc(line[j]))
330
- lines[i] = line
331
- if len(max_width) == 1: return '\n'.join(map(lambda x: x[0], lines))
332
-
333
- # 3 重组内容
334
- for i, line in enumerate(lines):
335
- for j in range(len(line) - 1): line[j] += ' ' * (max_width[j] - lenfunc(line[j])) # 注意最后一列就不用加空格了
336
- lines[i] = sep.join(line)
337
- return '\n'.join(lines)
338
-
339
-
340
- class Stdout:
341
- """重定向标准输出流,切换print标准输出位置
342
-
343
- 使用with语法调用
344
- """
345
-
346
- def __init__(self, path=None, mode='w'):
347
- """
348
- :param path: 可选参数
349
- 如果是一个合法的文件名,在__exit__时,会将结果写入文件
350
- 如果不合法不报错,只是没有功能效果
351
- :param mode: 写入模式
352
- 'w': 默认模式,直接覆盖写入
353
- 'a': 追加写入
354
- """
355
- self.origin_stdout = sys.stdout
356
- self._path = path
357
- self._mode = mode
358
- self.strout = io.StringIO()
359
- self.result = None
360
-
361
- def __enter__(self):
362
- sys.stdout = self.strout
363
- return self
364
-
365
- def __exit__(self, exc_type, exc_val, exc_tb):
366
- sys.stdout = self.origin_stdout
367
- self.result = str(self)
368
-
369
- # 如果输入的是一个合法的文件名,则将中间结果写入
370
- if not self._path:
371
- return
372
-
373
- try:
374
- with open(self._path, self._mode) as f:
375
- f.write(self.result)
376
- except TypeError as e:
377
- logging.exception(e)
378
- except FileNotFoundError as e:
379
- logging.exception(e)
380
-
381
- self.strout.close()
382
-
383
- def __str__(self):
384
- """在这个期间获得的文本内容"""
385
- if self.result:
386
- return self.result
387
- else:
388
- return self.strout.getvalue()
389
-
390
-
391
- def listalign(ls, fmt='r', *, width=None, fillchar=' ', prefix='', suffix='', chinese_char_width=2):
392
- """文档: https://blog.csdn.net/code4101/article/details/80985218(不过文档有些过时了)
393
-
394
- listalign列表对齐
395
- py3中str的len是计算字符数量,例如len('ab') --> 2, len('a中b') --> 3。
396
- 但在对齐等操作中,是需要将每个汉字当成宽度2来处理,计算字符串实际宽度的。
397
- 所以我们需要开发一个strwidth函数,效果: strwidth('ab') --> 2,strwidth('a中b') --> 4。
398
-
399
- :param ls:
400
- 要处理的列表,会对所有元素调用str处理,确保全部转为string类型
401
- 且会将换行符转为\n显示
402
- :param fmt: (format)
403
- l: left,左对齐
404
- c: center,居中
405
- r: right,右对齐
406
- 多个字符: 扩展fmt长度跟ls一样,每一个元素单独设置对齐格式。如果fmt长度小于ls,则扩展的格式按照fmt[-1]设置
407
- :param width:
408
- None或者设置值小于最长字符串: 不设域宽,直接按照最长的字符串为准
409
- :param fillchar: 填充字符
410
- :param prefix: 添加前缀
411
- :param suffix: 添加后缀
412
- :param chinese_char_width: 每个汉字字符宽度
413
-
414
- :return:
415
- 对齐后的数组ls,每个元素会转为str类型
416
-
417
- >>> listalign(['a', '哈哈', 'ccd'])
418
- [' a', '哈哈', ' ccd']
419
- >>> listalign(['a', '哈哈', 'ccd'], chinese_char_width=1.8)
420
- [' a', '   哈哈', ' ccd']
421
- """
422
- # 1 处理fmt数组
423
- if len(fmt) == 1:
424
- fmt = [fmt] * len(ls)
425
- elif len(fmt) < len(ls):
426
- fmt = list(fmt) + [fmt[-1]] * (len(ls) - len(fmt))
427
-
428
- # 2 算出需要域宽
429
- if chinese_char_width == 2:
430
- strs = [str(x).replace('\n', r'\n') for x in ls] # 存储转成字符串的元素
431
- lens = [strwidth(x) for x in strs] # 存储每个元素的实际域宽
432
- else:
433
- strs = [] # 存储转成字符串的元素
434
- lens = [] # 存储每个元素的实际域宽
435
- for i, t in enumerate(ls):
436
- t, n = strwidth_proc(t, fmt[i], chinese_char_width)
437
- strs.append(t)
438
- lens.append(n)
439
- w = max(lens)
440
- if width and isinstance(width, int) and width > w:
441
- w = width
442
-
443
- # 3 对齐操作
444
- for i, s in enumerate(strs):
445
- if fmt[i] == 'r':
446
- strs[i] = fillchar * (w - lens[i]) + strs[i]
447
- elif fmt[i] == 'l':
448
- strs[i] = strs[i] + fillchar * (w - lens[i])
449
- elif fmt[i] == 'c':
450
- t = w - lens[i]
451
- strs[i] = fillchar * (t - t // 2) + strs[i] + fillchar * (t // 2)
452
- strs[i] = prefix + strs[i] + suffix
453
- return strs
454
-
455
-
456
- def len_in_dim2_min(arr):
457
- """ 计算类List结构在第2维上的最小长度
458
-
459
- >>> len_in_dim2([[1,1], [2], [3,3,3]])
460
- 3
461
-
462
- >>> len_in_dim2([1, 2, 3]) # TODO 是不是应该改成0合理?但不知道牵涉到哪些功能影响
463
- 1
464
- """
465
- if not isinstance(arr, (list, tuple)):
466
- raise TypeError('类型错误,不是list构成的二维数组')
467
-
468
- # 找出元素最多的列
469
- column_num = math.inf
470
- for i, item in enumerate(arr):
471
- if isinstance(item, (list, tuple)): # 该行是一个一维数组
472
- column_num = min(column_num, len(item))
473
- else: # 如果不是数组,是指单个元素,当成1列处理
474
- column_num = min(column_num, 1)
475
- break # 只要有个1,最小长度就一定是1了
476
-
477
- return column_num
478
-
479
-
480
- def len_in_dim2(arr):
481
- """ 计算类List结构在第2维上的最大长度
482
-
483
- >>> len_in_dim2([[1,1], [2], [3,3,3]])
484
- 3
485
-
486
- >>> len_in_dim2([1, 2, 3]) # TODO 是不是应该改成0合理?但不知道牵涉到哪些功能影响
487
- 1
488
- """
489
- if not isinstance(arr, (list, tuple)):
490
- raise TypeError('类型错误,不是list构成的二维数组')
491
-
492
- # 找出元素最多的列
493
- column_num = 0
494
- for i, item in enumerate(arr):
495
- if isinstance(item, (list, tuple)): # 该行是一个一维数组
496
- column_num = max(column_num, len(item))
497
- else: # 如果不是数组,是指单个元素,当成1列处理
498
- column_num = max(column_num, 1)
499
-
500
- return column_num
501
-
502
-
503
- def ensure_array(arr, default_value=''):
504
- """对一个由list、tuple组成的二维数组,确保所有第二维的列数都相同
505
-
506
- >>> ensure_array([[1,1], [2], [3,3,3]])
507
- [[1, 1, ''], [2, '', ''], [3, 3, 3]]
508
- """
509
- max_cols = len_in_dim2(arr)
510
- if max_cols == 1:
511
- return arr
512
- dv = str(default_value)
513
- a = [[]] * len(arr)
514
- for i, ls in enumerate(arr):
515
- if isinstance(ls, (list, tuple)):
516
- t = list(arr[i])
517
- else:
518
- t = [ls] # 如果不是数组,是指单个元素,当成1列处理
519
- a[i] = t + [dv] * (max_cols - len(t)) # 左边的写list,是防止有的情况是tuple,要强制转list后拼接
520
- return a
521
-
522
-
523
- def swap_rowcol(a, *, ensure_arr=False, default_value=''):
524
- """矩阵行列互换
525
-
526
- 注:如果列数是不均匀的,则会以最小列数作为行数
527
-
528
- >>> swap_rowcol([[1,2,3], [4,5,6]])
529
- [[1, 4], [2, 5], [3, 6]]
530
- """
531
- if ensure_arr:
532
- a = ensure_array(a, default_value)
533
- # 这是非常有教学意义的行列互换实现代码
534
- return list(map(list, zip(*a)))
535
-
536
-
537
- def int2excel_col_name(d):
538
- """
539
- >>> int2excel_col_name(1)
540
- 'A'
541
- >>> int2excel_col_name(28)
542
- 'AB'
543
- >>> int2excel_col_name(100)
544
- 'CV'
545
- """
546
- s = []
547
- while d:
548
- t = (d - 1) % 26
549
- s.append(chr(65 + t))
550
- d = (d - 1) // 26
551
- return ''.join(reversed(s))
552
-
553
-
554
- def excel_col_name2int(s):
555
- """
556
- >>> excel_col_name2int('A')
557
- 1
558
- >>> excel_col_name2int('AA')
559
- 27
560
- >>> excel_col_name2int('AB')
561
- 28
562
- """
563
- d = 0
564
- for ch in s:
565
- d = d * 26 + (ord(ch) - 64)
566
- return d
567
-
568
-
569
- def int2myalphaenum(n):
570
- """
571
- :param n: 0~52的数字
572
- """
573
- if 0 <= n <= 52:
574
- return '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[n]
575
- else:
576
- print('警告:不在处理范围内的数值', n)
577
- raise ValueError
578
-
579
-
580
- def gentuple(n, tag):
581
- """有点类似range函数,但生成的数列更加灵活
582
- :param n:
583
- 数组长度
584
- :param tag:
585
- 整数,从指定整数开始编号
586
- int类型,从指定数字开始编号
587
- 0,从0开始编号
588
- 1,从1开始编号
589
- 'A',用Excel的形式编号
590
- tuple,按枚举值循环显示
591
- ('A', 'B'):循环使用A、B编号
592
-
593
- >>> gentuple(4, 'A')
594
- ('A', 'B', 'C', 'D')
595
- """
596
- a = [''] * n
597
- if isinstance(tag, int):
598
- for i in range(n):
599
- a[i] = i + tag
600
- elif tag == 'A':
601
- a = tuple(map(lambda x: int2excel_col_name(x + 1), range(n)))
602
- elif isinstance(tag, (list, tuple)):
603
- k = len(tag)
604
- a = tuple(map(lambda x: tag[x % k], range(n)))
605
- return a
606
-
607
-
608
- def ensure_gbk(s):
609
- """检查一个字符串的所有内容是否能正常转为gbk,
610
- 如果不能则ignore掉不能转换的部分"""
611
- try:
612
- s.encode('gbk')
613
- except UnicodeEncodeError:
614
- origin_s = s
615
- s = s.encode('gbk', errors='ignore').decode('gbk')
616
- print('警告:字符串存在无法转为gbk的字符', origin_s, s)
617
- return s
618
-
619
-
620
- def funcmsg(func):
621
- """输出函数func所在的文件、函数名、函数起始行"""
622
- # showdir(func)
623
- if not hasattr(func, '__name__'): # 没有__name__属性表示这很可能是一个装饰器去处理原函数了
624
- if hasattr(func, 'func'): # 我的装饰器常用func成员存储原函数对象
625
- func = func.func
626
- else:
627
- return f'装饰器:{type(func)},无法定位'
628
- return f'函数名:{func.__name__},来自文件:{func.__code__.co_filename},所在行号={func.__code__.co_firstlineno}'
629
-
630
-
631
- class GrowingList(list):
632
- """可变长list"""
633
-
634
- def __init__(self, default_value=None):
635
- super().__init__(self)
636
- self.default_value = default_value
637
-
638
- def __getitem__(self, index):
639
- if index >= len(self):
640
- self.extend([self.default_value] * (index + 1 - len(self)))
641
- return list.__getitem__(self, index)
642
-
643
- def __setitem__(self, index, value):
644
- if index >= len(self):
645
- self.extend([self.default_value] * (index + 1 - len(self)))
646
- list.__setitem__(self, index, value)
647
-
648
-
649
- def arr_hangclear(arr, depth=None):
650
- """ 清除连续相同值,简化表格内容
651
- >> arr_hangclear(arr, depth=2)
652
- 原表格:
653
- A B D
654
- A B E
655
- A C E
656
- A C E
657
- 新表格:
658
- A B D
659
- E
660
- C E
661
- E
662
-
663
- :param arr: 二维数组
664
- :param depth: 处理列上限
665
- 例如depth=1,则只处理第一层
666
- depth=None,则处理所有列
667
-
668
- >>> arr_hangclear([[1, 2, 4], [1, 2, 5], [1, 3, 5], [1, 3, 5]])
669
- [[1, 2, 4], ['', '', 5], ['', 3, 5], ['', '', 5]]
670
- >>> arr_hangclear([[1, 2, 4], [1, 2, 5], [2, 2, 5], [1, 2, 5]])
671
- [[1, 2, 4], ['', '', 5], [2, 2, 5], [1, 2, 5]]
672
- """
673
- m = depth or len_in_dim2(arr) - 1
674
- a = copy.deepcopy(arr)
675
-
676
- # 算法原理:从下到上,从右到左判断与上一行重叠了几列数据
677
- for i in range(len(arr) - 1, 0, -1):
678
- for j in range(m):
679
- if a[i][j] == a[i - 1][j]:
680
- a[i][j] = ''
681
- else:
682
- break
683
- return a
684
-
685
-
686
- def arr2table(arr, rowmerge=False):
687
- """数组转html表格代码
688
- :param arr: 需要处理的数组
689
- :param rowmerge: 行单元格合并
690
- :return: html文本格式的<table>
691
-
692
- 这个arr2table是用来画合并单元格的
693
- >> chrome(arr2table([['A', 1, 'a'], ['', 2, 'b'], ['B', 3, 'c'], ['', '', 'd'], ['', 5, 'e']], True), 'a.html')
694
- 效果图:http://i1.fuimg.com/582188/c452f40b5a072f8d.png
695
- """
696
- n = len(arr)
697
- m = len_in_dim2(arr)
698
- res = ['<table border="1"><tbody>']
699
- for i, line in enumerate(arr):
700
- res.append('<tr>')
701
- for j, ele in enumerate(line):
702
- if rowmerge:
703
- if ele != '':
704
- cnt = 1
705
- while i + cnt < n and arr[i + cnt][j] == '':
706
- for k in range(j - 1, -1, -1):
707
- if arr[i + cnt][k] != '':
708
- break
709
- else:
710
- cnt += 1
711
- continue
712
- break
713
- if cnt > 1:
714
- res.append(f'<td rowspan="{cnt}">{ele}</td>')
715
- else:
716
- res.append(f'<td>{ele}</td>')
717
- elif j == m - 1:
718
- res.append(f'<td>{ele}</td>')
719
- else:
720
- res.append(f'<td>{ele}</td>')
721
- res.append('</tr>')
722
- res.append('</tbody></table>')
723
- return ''.join(res)
724
-
725
-
726
- def digit2weektag(d):
727
- """输入数字1~7,转为“周一~周日”
728
-
729
- >>> digit2weektag(1)
730
- '周一'
731
- >>> digit2weektag('7')
732
- '周日'
733
- """
734
- d = int(d)
735
- if 1 <= d <= 7:
736
- return '周' + '一二三四五六日'[d - 1]
737
- else:
738
- raise ValueError
739
-
740
-
741
- def print2string(*args, **kwargs):
742
- """https://stackoverflow.com/questions/39823303/python3-print-to-string"""
743
- output = io.StringIO()
744
- print(*args, file=output, **kwargs)
745
- contents = output.getvalue()
746
- output.close()
747
- return contents
748
-
749
-
750
- def fullwidth2halfwidth(ustring):
751
- """ 把字符串全角转半角
752
-
753
- python3环境下的全角与半角转换代码和测试_大数据挖掘SparkExpert的博客-CSDN博客:
754
- https://blog.csdn.net/sparkexpert/article/details/82749207
755
-
756
- >>> fullwidth2halfwidth("你好pythonabdalduizxcvbnm")
757
- '你好pythonabdalduizxcvbnm'
758
- """
759
- ss = []
760
- for s in ustring:
761
- for uchar in s:
762
- inside_code = ord(uchar)
763
- if inside_code == 12288: # 全角空格直接转换
764
- inside_code = 32
765
- elif 65281 <= inside_code <= 65374: # 全角字符(除空格)根据关系转化
766
- inside_code -= 65248
767
- ss.append(chr(inside_code))
768
- return ''.join(ss)
769
-
770
-
771
- def fullwidth2halfwidth2(ustring):
772
- """ 不处理标点符号的版本
773
-
774
- >>> fullwidth2halfwidth2("你好pythonabda,lduizxcvbnm")
775
- '你好pythonabda,lduizxcvbnm'
776
- """
777
- ss = []
778
- for s in ustring:
779
- for uchar in s:
780
- if uchar in ':;!(),?".':
781
- ss.append(uchar)
782
- else:
783
- inside_code = ord(uchar)
784
- if inside_code == 12288: # 全角空格直接转换
785
- inside_code = 32
786
- elif 65281 <= inside_code <= 65374: # 全角字符(除空格)根据关系转化
787
- inside_code -= 65248
788
- ss.append(chr(inside_code))
789
- return ''.join(ss)
790
-
791
-
792
- def halfwidth2fullwidth(ustring):
793
- """ 把字符串全角转半角
794
-
795
- >>> halfwidth2fullwidth("你好pythonabdalduizxcvbnm")
796
- '你好pythonabdalduizxcvbnm'
797
- """
798
- ss = []
799
- for s in ustring:
800
- for uchar in s:
801
- inside_code = ord(uchar)
802
- if inside_code == 32: # 全角空格直接转换
803
- inside_code = 12288
804
- elif 33 <= inside_code <= 126: # 全角字符(除空格)根据关系转化
805
- inside_code += 65248
806
- ss.append(chr(inside_code))
807
- return ''.join(ss)
808
-
809
-
810
- def sort_by_given_list(a, b):
811
- r"""本函数一般用在数据透视表中,分组中元素名为中文,没有按指定规律排序的情况
812
- :param a: 需要排序的对象
813
- :param b: 参照的排序数组
814
- :return: 排序后的a
815
-
816
- >>> sort_by_given_list(['初中', '小学', '高中'], ['少儿', '小学', '初中', '高中'])
817
- ['小学', '初中', '高中']
818
-
819
- # 不在枚举项目里的,会统一列在最后面
820
- >>> sort_by_given_list(['初中', '小学', '高中', '幼儿'], ['少儿', '小学', '初中', '高中'])
821
- ['小学', '初中', '高中', '幼儿']
822
- """
823
- # 1 从b数组构造一个d字典,d[k]=i,值为k的元素在第i位
824
- d = dict()
825
- for i, bb in enumerate(b): d[bb] = i
826
- # 2 a数组分两部分,可以通过d排序的a1,和不能通过d排序的a2
827
- a1, a2 = [], []
828
- for aa in a:
829
- if aa in d:
830
- a1.append(aa)
831
- else:
832
- a2.append(aa)
833
- # 3 用不同的规则排序a1、a2后合并
834
- a1 = sorted(a1, key=lambda x: d[x])
835
- a2 = sorted(a2)
836
- return a1 + a2
837
-
838
-
839
- ____disjoint_set = """
840
- 并查集相关功能
841
- """
842
-
843
-
844
- def disjoint_set(items, join_checker):
845
- """ 按照一定的相连规则分组
846
-
847
- :param items: 项目清单
848
- :param join_checker: 检查任意两个对象是否相连,进行分组
849
- :return:
850
-
851
- 算法:因为会转成下标,按照下标进行分组合并,所以支持items里有重复值,或者unhashable对象
852
-
853
- >>> disjoint_set([-1, -2, 2, 0, 0, 1], lambda x, y: x*y>0)
854
- [[-1, -2], [2, 1], [0], [0]]
855
- """
856
- from itertools import combinations
857
-
858
- # 1 添加元素
859
- ds = DisjointSet()
860
- items = tuple(items)
861
- n = len(items)
862
- for i in range(n):
863
- ds.find(i)
864
-
865
- # 2 连接、分组
866
- for i, j in combinations(range(n), 2):
867
- if join_checker(items[i], items[j]):
868
- ds.union(i, j)
869
-
870
- # 3 返回分组信息
871
- res = []
872
- for group in ds.itersets():
873
- group_elements = [items[g] for g in group]
874
- res.append(group_elements)
875
- return res
876
-
877
-
878
- ____other = """
879
- """
880
-
881
-
882
- def xlbool(v):
883
- """ 有些类型不能直接判断真假,例如具有多值的np.array,df等
884
-
885
- 这些有歧义的情况,在我的mybool里暂时都判断为True,如果有需要,需要精细化判断,可以扩展自己的npbool、dfbool
886
- """
887
- try:
888
- return bool(v)
889
- except ValueError:
890
- return True
891
-
892
-
893
- def struct_unpack(f, fmt):
894
- r""" 类似np.fromfile的功能,读取并解析二进制数据
895
-
896
- :param f:
897
- 如果带有read方法,则用read方法读取指定字节数
898
- 如果bytes对象则直接处理
899
- :param fmt: 格式
900
- 默认按小端解析(2, 1, 0, 0) -> 258,如果需要大端,可以加前缀'>'
901
- 字节:c=char, b=signed char, B=unsigned char, ?=bool
902
- 2字节整数:h=short, H=unsigned short(后文同理,大写只是变成unsigned模式,不在累述)
903
- 4字节整数:i, I, l, L
904
- 8字节整数:q, Q
905
- 浮点数:e=2字节,f=4字节,d=8字节
906
-
907
- >>> b = struct.pack('B', 127)
908
- >>> b
909
- b'\x7f'
910
- >>> struct_unpack(b, 'c')
911
- (b'\x7f',)
912
- >>> struct_unpack(b, 'B')
913
- (127,)
914
-
915
- >>> b = struct.pack('I', 258)
916
- >>> b
917
- b'\x02\x01\x00\x00'
918
- >>> struct_unpack(b, 'I') # 默认都是按小端打包、解析
919
- (258,)
920
- >>> struct_unpack(b, '>I') # 错误示范,按大端解析的值
921
- (33619968,)
922
- >>> struct_unpack(b, 'H'*2) # 解析两个值,fmt*2即可
923
- (258, 0)
924
-
925
- >>> f = io.BytesIO(b'\x02\x01\x03\x04')
926
- >>> struct_unpack(f, 'B'*3) # 取前3个值,等价于np.fromfile(f, dtype='uint8', count=3)
927
- (2, 1, 3)
928
- >>> struct_unpack(f, 'B') # 取出第4个值
929
- (4,)
930
- """
931
- # 1 取数据
932
- size_ = struct.calcsize(fmt)
933
- if hasattr(f, 'read'):
934
- data = f.read(size_)
935
- if len(data) < size_:
936
- raise ValueError(f'剩余数据长度 {len(data)} 小于 fmt 需要的长度 {size_}')
937
- else: # 对于bytes等矩阵,可以多输入,但是只解析前面一部分
938
- data = f[:size_]
939
-
940
- # 2 解析
941
- res = struct.unpack(fmt, data)
942
- if len(res) == 1: # 解析结果恰好只有一个的时候,返回值本身
943
- return res[0]
944
- else:
945
- return res