bigarraylist 0.2.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.
- bigarraylist/__init__.py +70 -0
- bigarraylist/_api.py +453 -0
- bigarraylist/algorithms/__init__.py +22 -0
- bigarraylist/algorithms/bucket.py +200 -0
- bigarraylist/algorithms/external_sort.py +200 -0
- bigarraylist/algorithms/parallel.py +68 -0
- bigarraylist/algorithms/sample.py +51 -0
- bigarraylist/config.py +86 -0
- bigarraylist/core/__init__.py +5 -0
- bigarraylist/core/base.py +223 -0
- bigarraylist/exceptions.py +50 -0
- bigarraylist/operators/__init__.py +14 -0
- bigarraylist/operators/crud.py +89 -0
- bigarraylist/operators/search.py +212 -0
- bigarraylist/operators/sort.py +212 -0
- bigarraylist/operators/stream.py +341 -0
- bigarraylist/utils/__init__.py +45 -0
- bigarraylist/utils/scale.py +139 -0
- bigarraylist/utils/validation.py +171 -0
- bigarraylist-0.2.0.dist-info/METADATA +158 -0
- bigarraylist-0.2.0.dist-info/RECORD +24 -0
- bigarraylist-0.2.0.dist-info/WHEEL +5 -0
- bigarraylist-0.2.0.dist-info/licenses/LICENSE +21 -0
- bigarraylist-0.2.0.dist-info/top_level.txt +1 -0
bigarraylist/__init__.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""BigArrayList:面向大数据量场景的惰性流式列表工具包。
|
|
2
|
+
|
|
3
|
+
公共 API:
|
|
4
|
+
BigArrayList —— 用户接口层(增删改插 / 查找 / 排序 / 流式算子 / 链式 API)
|
|
5
|
+
BigArrayConfig —— 阈值配置对象(可调外部排序模式、桶上限等)
|
|
6
|
+
LazyChain —— 链式算子(big.lazy().filter().map().collect())
|
|
7
|
+
BucketIndex —— 持久化分桶索引(build_buckets 返回,多次 find 复用)
|
|
8
|
+
exceptions —— 自定义异常层次(继承标准异常,向后兼容)
|
|
9
|
+
|
|
10
|
+
ScaleHelper / ScaleValue / choose_chunk_size / choose_external_sort_chunk_size /
|
|
11
|
+
chunk_wise_processing —— 向后兼容保留
|
|
12
|
+
|
|
13
|
+
典型用法:
|
|
14
|
+
from bigarraylist import BigArrayList
|
|
15
|
+
arr = BigArrayList([3, 1, 4, 1, 5, 9, 2, 6])
|
|
16
|
+
for x in arr.sort():
|
|
17
|
+
print(x)
|
|
18
|
+
|
|
19
|
+
# 链式 API(新功能)
|
|
20
|
+
result = (arr.lazy()
|
|
21
|
+
.filter(lambda x: x > 2)
|
|
22
|
+
.map(lambda x: x * 10)
|
|
23
|
+
.distinct()
|
|
24
|
+
.collect())
|
|
25
|
+
|
|
26
|
+
# 持久化分桶(多次查询复用)
|
|
27
|
+
with arr.build_buckets() as idx:
|
|
28
|
+
for tgt in targets:
|
|
29
|
+
hits = list(idx.find(tgt))
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from ._api import BigArrayList
|
|
33
|
+
from .algorithms.bucket import BucketIndex, build_buckets
|
|
34
|
+
from .config import BigArrayConfig
|
|
35
|
+
from .exceptions import (
|
|
36
|
+
BigArrayListError,
|
|
37
|
+
DataSourceError,
|
|
38
|
+
IndexOutOfRangeError,
|
|
39
|
+
InvalidArgumentError,
|
|
40
|
+
)
|
|
41
|
+
from .operators.stream import LazyChain
|
|
42
|
+
from .utils.scale import (
|
|
43
|
+
ScaleHelper,
|
|
44
|
+
ScaleValue,
|
|
45
|
+
choose_chunk_size,
|
|
46
|
+
choose_external_sort_chunk_size,
|
|
47
|
+
chunk_wise_processing,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"BigArrayConfig",
|
|
52
|
+
# 主类
|
|
53
|
+
"BigArrayList",
|
|
54
|
+
# 异常
|
|
55
|
+
"BigArrayListError",
|
|
56
|
+
"BucketIndex",
|
|
57
|
+
"DataSourceError",
|
|
58
|
+
"IndexOutOfRangeError",
|
|
59
|
+
"InvalidArgumentError",
|
|
60
|
+
"LazyChain",
|
|
61
|
+
# 向后兼容:量级助手
|
|
62
|
+
"ScaleHelper",
|
|
63
|
+
"ScaleValue",
|
|
64
|
+
"build_buckets",
|
|
65
|
+
"choose_chunk_size",
|
|
66
|
+
"choose_external_sort_chunk_size",
|
|
67
|
+
"chunk_wise_processing",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
__version__ = "0.2.0"
|
bigarraylist/_api.py
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""BigArrayList 用户接口层。
|
|
2
|
+
|
|
3
|
+
设计要点(重构后):
|
|
4
|
+
- 仍继承 BaseBigArrayList(拆分为 mixin 后的核心 + 算子组合),
|
|
5
|
+
所有公开方法签名 100% 向后兼容。
|
|
6
|
+
- __init__ 新增可选 config 参数(BigArrayConfig),不传时用默认配置——
|
|
7
|
+
行为与重构前完全一致。
|
|
8
|
+
- 校验函数改用 utils.validation 模块;默认抛标准异常(向后兼容),
|
|
9
|
+
设置 raise_custom=True 时抛自定义异常子类(仍继承对应标准异常)。
|
|
10
|
+
- 删除死代码:__data_scale 字段 + get_scale_helper() 方法。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Callable, Generator, Iterable
|
|
16
|
+
from typing import Any, TypeVar
|
|
17
|
+
|
|
18
|
+
from .algorithms.bucket import BucketIndex
|
|
19
|
+
from .config import BigArrayConfig
|
|
20
|
+
from .core.base import BaseBigArrayList
|
|
21
|
+
from .operators.stream import LazyChain
|
|
22
|
+
from .utils.scale import ScaleHelper
|
|
23
|
+
from .utils.validation import (
|
|
24
|
+
validate_callable,
|
|
25
|
+
validate_index,
|
|
26
|
+
validate_iterable,
|
|
27
|
+
validate_max_workers,
|
|
28
|
+
validate_num_buckets,
|
|
29
|
+
validate_range,
|
|
30
|
+
validate_sample_k,
|
|
31
|
+
validate_slice_arg,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
T = TypeVar("T")
|
|
35
|
+
U = TypeVar("U")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BigArrayList(BaseBigArrayList):
|
|
39
|
+
"""大数据用户接口层。
|
|
40
|
+
|
|
41
|
+
继承自 BaseBigArrayList 的惰性流机制(核心 + CRUD/Sort/Search/Stream mixin),
|
|
42
|
+
在此基础上提供 append / remove / replace / insert / find / sort /
|
|
43
|
+
binary_search / reverse / copy 等用户级 API。
|
|
44
|
+
|
|
45
|
+
所有公共方法在入口处做参数校验(类型 + 范围),非法参数抛
|
|
46
|
+
TypeError / IndexError / ValueError,保证错误尽早暴露。
|
|
47
|
+
想要更精确的异常类型可设 `raise_custom=True`,会改抛
|
|
48
|
+
InvalidArgumentError / IndexOutOfRangeError / DataSourceError
|
|
49
|
+
(均继承对应标准异常)。
|
|
50
|
+
|
|
51
|
+
关于惰性与二分查找的冲突:
|
|
52
|
+
- 线性查找 (find) 可保持惰性:流式分块、查到就 yield、不消费剩余
|
|
53
|
+
- 二分查找 (binary_search) 要求"有序 + 随机访问",因此必须先排好序
|
|
54
|
+
并把结果物化到内存中。这是算法本身的约束,不是设计缺陷。
|
|
55
|
+
- 建议:同一批数据要多次查找时,用 binary_search(一次排序 + 多次 O(log n));
|
|
56
|
+
只查一次或少量几次,用 find(不用付排序成本)。
|
|
57
|
+
|
|
58
|
+
关于 reverse / copy 的 source 类型限制:
|
|
59
|
+
- int / list / tuple source:完全支持(list/tuple 走 reversed() 视图,不拷贝)
|
|
60
|
+
- generator source:源在 __init__ 中已被 list(...) 消费耗尽,reverse / copy
|
|
61
|
+
会抛 TypeError——请用 list/tuple 构造以支持这两个操作
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
# 子类新增字段:
|
|
65
|
+
# - __raise_custom:是否抛自定义异常(默认 False 保持向后兼容)
|
|
66
|
+
# 注意:父类已声明 __data/__len/__index/__source/__config,
|
|
67
|
+
# 子类不能再在自己的 __slots__ 里重复声明这些双下划线名字。
|
|
68
|
+
__slots__ = ("__raise_custom",)
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
source: int | Iterable[T],
|
|
73
|
+
*,
|
|
74
|
+
config: BigArrayConfig | None = None,
|
|
75
|
+
raise_custom: bool = False,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""构造 BigArrayList。
|
|
78
|
+
|
|
79
|
+
参数:
|
|
80
|
+
source: int / list / tuple / 任意可迭代对象
|
|
81
|
+
(int 表示造 N 个全 0 元素,大数据惰性测试用)
|
|
82
|
+
config: BigArrayConfig 实例;None 用默认配置
|
|
83
|
+
raise_custom: True 时校验失败抛自定义异常子类
|
|
84
|
+
(InvalidArgumentError / IndexOutOfRangeError / DataSourceError,
|
|
85
|
+
均继承 ValueError/IndexError/TypeError),False 抛标准异常
|
|
86
|
+
"""
|
|
87
|
+
# source 是 None 时父类会抛 TypeError;这里先给清晰错误
|
|
88
|
+
if source is None:
|
|
89
|
+
raise TypeError("source 不能为 None,请传入 int / list / tuple / 迭代器")
|
|
90
|
+
self.__raise_custom = raise_custom
|
|
91
|
+
super().__init__(source, config=config)
|
|
92
|
+
|
|
93
|
+
# ==================== 校验辅助(委托 utils.validation)====================
|
|
94
|
+
def _v_index(self, name: str, value: object, length: int) -> None:
|
|
95
|
+
validate_index(name, value, length, use_custom=self.__raise_custom)
|
|
96
|
+
|
|
97
|
+
def _v_range(self, start: int, end: int) -> None:
|
|
98
|
+
validate_range(start, end, len(self), use_custom=self.__raise_custom)
|
|
99
|
+
|
|
100
|
+
def _v_max_workers(self, value: int | None) -> None:
|
|
101
|
+
validate_max_workers(value, use_custom=self.__raise_custom)
|
|
102
|
+
|
|
103
|
+
def _v_num_buckets(self, value: int | None) -> None:
|
|
104
|
+
validate_num_buckets(value, use_custom=self.__raise_custom)
|
|
105
|
+
|
|
106
|
+
def _v_sample_k(self, value: int) -> None:
|
|
107
|
+
validate_sample_k(value, use_custom=self.__raise_custom)
|
|
108
|
+
|
|
109
|
+
def _v_slice_arg(self, name: str, value: int | None) -> None:
|
|
110
|
+
validate_slice_arg(name, value, use_custom=self.__raise_custom)
|
|
111
|
+
|
|
112
|
+
def _v_callable(self, name: str, value: object) -> None:
|
|
113
|
+
validate_callable(name, value, use_custom=self.__raise_custom)
|
|
114
|
+
|
|
115
|
+
def _v_iterable(self, name: str, value: object) -> None:
|
|
116
|
+
validate_iterable(name, value, use_custom=self.__raise_custom)
|
|
117
|
+
|
|
118
|
+
# ===== 向后兼容:保留旧校验方法名(已迁移到 utils.validation) =====
|
|
119
|
+
# 这些方法在重构前是 BigArrayList 的私有静态方法,部分外部代码可能直接 import 调用。
|
|
120
|
+
@staticmethod
|
|
121
|
+
def _validate_index(name: str, value: object, length: int) -> None:
|
|
122
|
+
validate_index(name, value, length, use_custom=False)
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _validate_max_workers(value: int | None) -> None:
|
|
126
|
+
validate_max_workers(value, use_custom=False)
|
|
127
|
+
|
|
128
|
+
def _validate_range(self, start: int, end: int) -> None:
|
|
129
|
+
validate_range(start, end, len(self), use_custom=False)
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _validate_callable(name: str, value: object) -> None:
|
|
133
|
+
validate_callable(name, value, use_custom=False)
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def _validate_slice_arg(name: str, value: int | None, allow_none: bool = True) -> None:
|
|
137
|
+
# allow_none 参数保留以兼容旧签名;当前实现 None 总是合法
|
|
138
|
+
if value is None and not allow_none:
|
|
139
|
+
raise TypeError(f"{name} 不能为 None")
|
|
140
|
+
validate_slice_arg(name, value, use_custom=False)
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def _validate_iterable(name: str, value: object) -> None:
|
|
144
|
+
validate_iterable(name, value, use_custom=False)
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def _validate_num_buckets(value: int | None) -> None:
|
|
148
|
+
validate_num_buckets(value, use_custom=False)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _validate_sample_k(value: int) -> None:
|
|
152
|
+
validate_sample_k(value, use_custom=False)
|
|
153
|
+
|
|
154
|
+
# ==================== 增 ====================
|
|
155
|
+
def append(self, *new_items: T) -> Generator[T, None, None]:
|
|
156
|
+
"""惰性追加:在原流末尾接上新元素,返回新生成器。
|
|
157
|
+
|
|
158
|
+
注意:本方法是普通函数(非生成器函数),调用时立即完成参数处理
|
|
159
|
+
并返回内部生成器对象。返回值仍是惰性流。
|
|
160
|
+
"""
|
|
161
|
+
# new_items 为空也合法(等价于原样产出),不报错
|
|
162
|
+
return self._append_iter(*new_items)
|
|
163
|
+
|
|
164
|
+
# ==================== 删 ====================
|
|
165
|
+
def remove(self, start: int, end: int) -> Generator[T, None, None]:
|
|
166
|
+
"""惰性删除:跳过 [start, end) 范围的元素,其余照常产出。"""
|
|
167
|
+
self._v_range(start, end)
|
|
168
|
+
return self._remove_iter(start, end)
|
|
169
|
+
|
|
170
|
+
# ==================== 改 ====================
|
|
171
|
+
def replace(self, start: int, end: int, *new_items: T) -> Generator[T, None, None]:
|
|
172
|
+
"""惰性替换:把 [start, end) 范围的元素替换成 new_items。"""
|
|
173
|
+
self._v_range(start, end)
|
|
174
|
+
return self._replace_iter(start, end, *new_items)
|
|
175
|
+
|
|
176
|
+
def insert(self, index: int, *new_items: T) -> Generator[T, None, None]:
|
|
177
|
+
"""惰性插入:在 index 位置插入 new_items。"""
|
|
178
|
+
self._v_index("index", index, len(self))
|
|
179
|
+
return self._insert_iter(index, *new_items)
|
|
180
|
+
|
|
181
|
+
# ==================== 线性查找(惰性并行)====================
|
|
182
|
+
def find(
|
|
183
|
+
self, target: T, max_workers: int | None = None
|
|
184
|
+
) -> Generator[tuple[int, T], None, None]:
|
|
185
|
+
"""惰性并行线性查找:返回生成器,每项为 (全局下标, 命中元素)。
|
|
186
|
+
|
|
187
|
+
- 不预排序、不物化:流式分块、多进程并行
|
|
188
|
+
- 查到即 yield,配合 islice 只取前 k 个命中时,可早停省资源
|
|
189
|
+
- 单次 / 少量查找的首选
|
|
190
|
+
"""
|
|
191
|
+
self._v_max_workers(max_workers)
|
|
192
|
+
return self._parallel_find(target, max_workers=max_workers)
|
|
193
|
+
|
|
194
|
+
# ==================== 二分查找(先排序,再 bisect)====================
|
|
195
|
+
def binary_search(
|
|
196
|
+
self,
|
|
197
|
+
target: T,
|
|
198
|
+
max_workers: int | None = None,
|
|
199
|
+
key: Callable[[T], Any] | None = None,
|
|
200
|
+
) -> Generator[tuple[int, T], None, None]:
|
|
201
|
+
"""二分查找:返回生成器,每项为 (排序后下标, 命中元素)。
|
|
202
|
+
|
|
203
|
+
流程:
|
|
204
|
+
1) _big_iter_sort 并行分块排序 → 有序迭代器
|
|
205
|
+
2) 物化为 list(bisect 需要 arr[mid] 随机访问)
|
|
206
|
+
3) bisect_left / bisect_right 定位所有命中位置,逐个 yield
|
|
207
|
+
|
|
208
|
+
参数:
|
|
209
|
+
target: 要查找的元素;若 key 不为 None,则比较 key(target)
|
|
210
|
+
max_workers: 排序阶段进程池并行度
|
|
211
|
+
key: 排序键函数(类似 ``sorted(data, key=...)``);
|
|
212
|
+
None 按元素本身排序/查找。
|
|
213
|
+
⚠️ 多进程路径要求 key 可 pickle(模块级函数或 functools.partial)
|
|
214
|
+
|
|
215
|
+
注意:返回的下标是"**排序后**的下标",不是原始数据的下标。
|
|
216
|
+
"""
|
|
217
|
+
self._v_max_workers(max_workers)
|
|
218
|
+
if key is not None:
|
|
219
|
+
self._v_callable("key", key)
|
|
220
|
+
return self._binary_search(target, max_workers=max_workers, key=key)
|
|
221
|
+
|
|
222
|
+
# ==================== 分桶查找(哈希分桶 + 单桶扫描)====================
|
|
223
|
+
def bucket_find(
|
|
224
|
+
self,
|
|
225
|
+
target: T,
|
|
226
|
+
key_func: Callable[[T], Any] | None = None,
|
|
227
|
+
num_buckets: int | None = None,
|
|
228
|
+
max_workers: int | None = None,
|
|
229
|
+
) -> Generator[tuple[int, T], None, None]:
|
|
230
|
+
"""分桶查找:按 key 哈希值把数据分到 N 个桶,只扫目标桶定位命中。
|
|
231
|
+
|
|
232
|
+
适合"同一批数据要查很多次"的场景(建桶一次 O(n),之后每次 O(n/N))。
|
|
233
|
+
与 find 的区别:find 每次全量扫;bucket_find 第一次建桶,后续每次只扫单桶。
|
|
234
|
+
与 binary_search 的区别:返回原始下标(不是排序后下标),不需要排序。
|
|
235
|
+
|
|
236
|
+
⚠️ 本方法每次调用都重建桶(保持零副作用)。
|
|
237
|
+
要真正复用建桶成本,请改用 build_buckets() 返回持久化 BucketIndex。
|
|
238
|
+
"""
|
|
239
|
+
if key_func is not None:
|
|
240
|
+
self._v_callable("key_func", key_func)
|
|
241
|
+
self._v_num_buckets(num_buckets)
|
|
242
|
+
self._v_max_workers(max_workers)
|
|
243
|
+
return self._bucket_find(
|
|
244
|
+
target,
|
|
245
|
+
key_func=key_func,
|
|
246
|
+
num_buckets=num_buckets,
|
|
247
|
+
max_workers=max_workers,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def build_buckets(
|
|
251
|
+
self,
|
|
252
|
+
key_func: Callable[[T], Any] | None = None,
|
|
253
|
+
num_buckets: int | None = None,
|
|
254
|
+
) -> BucketIndex:
|
|
255
|
+
"""持久化分桶:建桶一次,返回可多次查询的 BucketIndex。
|
|
256
|
+
|
|
257
|
+
与 bucket_find 的区别:
|
|
258
|
+
- bucket_find:建桶 + 查一次 + 销毁桶,仅适合单次查
|
|
259
|
+
- build_buckets:建桶一次返回 BucketIndex,可多次 find(target)
|
|
260
|
+
|
|
261
|
+
使用:
|
|
262
|
+
with big.build_buckets(key_func=lambda u: u.id) as idx:
|
|
263
|
+
for tgt in targets:
|
|
264
|
+
hits = list(idx.find(tgt))
|
|
265
|
+
"""
|
|
266
|
+
if key_func is not None:
|
|
267
|
+
self._v_callable("key_func", key_func)
|
|
268
|
+
self._v_num_buckets(num_buckets)
|
|
269
|
+
return self._build_buckets(
|
|
270
|
+
key_func=key_func,
|
|
271
|
+
num_buckets=num_buckets,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# ==================== 蓄水池抽样 ====================
|
|
275
|
+
def sample(self, k: int, seed: int | None = None) -> Generator[T, None, None]:
|
|
276
|
+
"""蓄水池抽样:从总量未知的流里随机等概率抽 k 个元素。
|
|
277
|
+
|
|
278
|
+
一遍流式扫描,内存 O(k),不需要预先知道总量 N。
|
|
279
|
+
每个元素被选中的概率都是 k/N。
|
|
280
|
+
"""
|
|
281
|
+
self._v_sample_k(k)
|
|
282
|
+
return self._sample(k, seed=seed)
|
|
283
|
+
|
|
284
|
+
# ==================== 二级索引 ====================
|
|
285
|
+
def build_index(self, key_func: Callable[[T], Any] | None = None) -> dict:
|
|
286
|
+
"""建二级索引:dict[key] -> list[原始下标]。
|
|
287
|
+
|
|
288
|
+
建好的索引可反复用:
|
|
289
|
+
- 判断 key 在不在:O(1)
|
|
290
|
+
- 拿到所有命中下标列表:O(1)
|
|
291
|
+
|
|
292
|
+
注意:惰性流不支持随机访问(arr[i] 抛 NotImplementedError),
|
|
293
|
+
索引存的是下标,要拿元素仍需流式扫。
|
|
294
|
+
"""
|
|
295
|
+
if key_func is not None:
|
|
296
|
+
self._v_callable("key_func", key_func)
|
|
297
|
+
return self._build_index(key_func=key_func)
|
|
298
|
+
|
|
299
|
+
# ==================== 统一查找入口 ====================
|
|
300
|
+
def auto_find(
|
|
301
|
+
self,
|
|
302
|
+
target: T,
|
|
303
|
+
key_func: Callable[[T], Any] | None = None,
|
|
304
|
+
num_buckets: int | None = None,
|
|
305
|
+
max_workers: int | None = None,
|
|
306
|
+
) -> Generator[tuple[int, T], None, None]:
|
|
307
|
+
"""统一查找入口:根据数据量自动选策略。
|
|
308
|
+
|
|
309
|
+
策略:
|
|
310
|
+
- 小数据(< config.linear_parallel_threshold):线性查找
|
|
311
|
+
- 大数据(>= threshold):分桶查找
|
|
312
|
+
|
|
313
|
+
不做 binary_search(要排序+返回排序后下标,语义不同)。
|
|
314
|
+
不缓存预处理结果(零副作用;查很多次请直接用 build_buckets)。
|
|
315
|
+
"""
|
|
316
|
+
if key_func is not None:
|
|
317
|
+
self._v_callable("key_func", key_func)
|
|
318
|
+
self._v_num_buckets(num_buckets)
|
|
319
|
+
self._v_max_workers(max_workers)
|
|
320
|
+
return self._auto_find(
|
|
321
|
+
target,
|
|
322
|
+
key_func=key_func,
|
|
323
|
+
num_buckets=num_buckets,
|
|
324
|
+
max_workers=max_workers,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
# ==================== 排序 ====================
|
|
328
|
+
def sort(
|
|
329
|
+
self,
|
|
330
|
+
max_workers: int | None = None,
|
|
331
|
+
key: Callable[[T], Any] | None = None,
|
|
332
|
+
) -> Generator[T, None, None]:
|
|
333
|
+
"""惰性并行排序:返回生成器,按升序逐个产出。
|
|
334
|
+
|
|
335
|
+
参数:
|
|
336
|
+
max_workers: 进程池并行度;None 用默认
|
|
337
|
+
key: 排序键函数(类似 ``sorted(data, key=...)``);
|
|
338
|
+
None 按元素本身排序。
|
|
339
|
+
⚠️ 多进程路径(中数据/大数据)要求 key 可 pickle,
|
|
340
|
+
即模块级函数或 functools.partial;lambda 在 spawn
|
|
341
|
+
模式下不可 pickle,仅小数据路径(单进程)可用 lambda。
|
|
342
|
+
|
|
343
|
+
默认 streaming 模式:归并阶段逐条 load,内存峰值 = O(块数)。
|
|
344
|
+
设 BigArrayConfig(external_sort_streaming_merge=False) 走整块模式
|
|
345
|
+
(速度快 3~5 倍,但内存峰值 ≈ 数据总量)。
|
|
346
|
+
"""
|
|
347
|
+
self._v_max_workers(max_workers)
|
|
348
|
+
if key is not None:
|
|
349
|
+
self._v_callable("key", key)
|
|
350
|
+
return self._big_iter_sort(max_workers=max_workers, key=key)
|
|
351
|
+
|
|
352
|
+
# ==================== 反转 ====================
|
|
353
|
+
def reverse(self) -> Generator[T, None, None]:
|
|
354
|
+
"""惰性反转:从尾到头逐个产出元素。"""
|
|
355
|
+
return self._reverse_iter()
|
|
356
|
+
|
|
357
|
+
# ==================== 拷贝 ====================
|
|
358
|
+
def copy(self) -> BigArrayList:
|
|
359
|
+
"""返回一个包含相同数据的新 BigArrayList 实例(独立迭代器状态)。
|
|
360
|
+
|
|
361
|
+
实现策略(按 source 类型分流,与 reverse 一致):
|
|
362
|
+
- int source:直接用同样的 int 构造
|
|
363
|
+
- list/tuple source:直接复用源容器(list/tuple 可重复迭代)
|
|
364
|
+
- generator source:源已耗尽无法复制,抛 TypeError
|
|
365
|
+
"""
|
|
366
|
+
src = self._get_source() # 通过 _CoreBase 提供的保护方法访问
|
|
367
|
+
if isinstance(src, int):
|
|
368
|
+
return BigArrayList(src)
|
|
369
|
+
if isinstance(src, (list, tuple)):
|
|
370
|
+
return BigArrayList(src)
|
|
371
|
+
raise TypeError(
|
|
372
|
+
"无法对 generator source 执行 copy(源已耗尽);"
|
|
373
|
+
"请用 list/tuple 构造 BigArrayList(...) 以支持 copy"
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# ==================== 流式算子(裸 generator,向后兼容)====================
|
|
377
|
+
def map(self, func: Callable[[T], U]) -> Generator[U, None, None]:
|
|
378
|
+
"""惰性变换:对每个元素应用 func,产出结果流(裸 generator)。"""
|
|
379
|
+
self._v_callable("func", func)
|
|
380
|
+
return self._map_iter(func)
|
|
381
|
+
|
|
382
|
+
def filter(self, predicate: Callable[[T], bool]) -> Generator[T, None, None]:
|
|
383
|
+
"""惰性过滤:只产出 predicate(item) 为真的元素(裸 generator)。"""
|
|
384
|
+
self._v_callable("predicate", predicate)
|
|
385
|
+
return self._filter_iter(predicate)
|
|
386
|
+
|
|
387
|
+
def reduce(self, func: Callable[[U, T], U], initial: U | None = None) -> U:
|
|
388
|
+
"""流式聚合:acc = func(acc, item),逐元素累加,返回最终 acc。"""
|
|
389
|
+
self._v_callable("func", func)
|
|
390
|
+
return self._reduce(func, initial=initial)
|
|
391
|
+
|
|
392
|
+
def slice(
|
|
393
|
+
self,
|
|
394
|
+
start: int | None = None,
|
|
395
|
+
stop: int | None = None,
|
|
396
|
+
step: int | None = None,
|
|
397
|
+
) -> Generator[T, None, None]:
|
|
398
|
+
"""惰性切片:包装 itertools.islice,支持步长。
|
|
399
|
+
|
|
400
|
+
约束:start/stop/step 不允许负数(惰性流无法反向)。
|
|
401
|
+
"""
|
|
402
|
+
self._v_slice_arg("start", start)
|
|
403
|
+
self._v_slice_arg("stop", stop)
|
|
404
|
+
self._v_slice_arg("step", step)
|
|
405
|
+
if step is not None and step == 0:
|
|
406
|
+
raise ValueError("step 不能为 0")
|
|
407
|
+
return self._slice_iter(start, stop, step)
|
|
408
|
+
|
|
409
|
+
def distinct(self) -> Generator[T, None, None]:
|
|
410
|
+
"""惰性去重:保留每个元素的首次出现(裸 generator)。"""
|
|
411
|
+
return self._distinct_iter()
|
|
412
|
+
|
|
413
|
+
def concat(self, other: Iterable[T]) -> Generator[T, None, None]:
|
|
414
|
+
"""惰性合并:把 self 和 other 首尾相接产出(裸 generator)。"""
|
|
415
|
+
self._v_iterable("other", other)
|
|
416
|
+
return self._concat_iter(other)
|
|
417
|
+
|
|
418
|
+
def chain(self, *others: Iterable[T]) -> Generator[T, None, None]:
|
|
419
|
+
"""惰性链式合并:把 self 和多个 others 首尾相接产出(裸 generator)。"""
|
|
420
|
+
for i, o in enumerate(others):
|
|
421
|
+
self._v_iterable(f"others[{i}]", o)
|
|
422
|
+
return self._chain_iter(*others)
|
|
423
|
+
|
|
424
|
+
def group_by(self, key_func: Callable[[T], Any]) -> Generator[tuple[Any, list[T]], None, None]:
|
|
425
|
+
"""SQL 风格 GROUP BY:按 key_func 分组,yield (key, list[elements])。"""
|
|
426
|
+
self._v_callable("key_func", key_func)
|
|
427
|
+
return self._group_by_iter(key_func)
|
|
428
|
+
|
|
429
|
+
def count_if(self, predicate: Callable[[T], bool]) -> int:
|
|
430
|
+
"""流式条件计数:统计 predicate(item) 为真的元素个数。"""
|
|
431
|
+
self._v_callable("predicate", predicate)
|
|
432
|
+
return self._count_if(predicate)
|
|
433
|
+
|
|
434
|
+
# ==================== 链式 API(新功能,不破坏旧 API)====================
|
|
435
|
+
def lazy(self) -> LazyChain[T]:
|
|
436
|
+
"""切换到链式 API:返回 LazyChain 包装的 self._fresh_iter()。
|
|
437
|
+
|
|
438
|
+
用法:
|
|
439
|
+
result = (big.lazy()
|
|
440
|
+
.filter(lambda x: x > 0)
|
|
441
|
+
.map(lambda x: x * 2)
|
|
442
|
+
.distinct()
|
|
443
|
+
.slice(0, 10)
|
|
444
|
+
.collect()) # 物化为 list
|
|
445
|
+
|
|
446
|
+
与旧 .map() / .filter() 的区别:
|
|
447
|
+
- 旧方法返回裸 generator,不能 .filter().map() 链式
|
|
448
|
+
- .lazy() 返回 LazyChain,每个算子返回新 LazyChain,可链式组合
|
|
449
|
+
"""
|
|
450
|
+
return super().lazy()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
__all__ = ["BigArrayConfig", "BigArrayList", "BucketIndex", "LazyChain", "ScaleHelper"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""算法层:模块级可 pickle 函数 / 外部排序 / 持久化分桶 / 蓄水池抽样。"""
|
|
2
|
+
|
|
3
|
+
from .bucket import BucketIndex, build_buckets
|
|
4
|
+
from .external_sort import external_sort
|
|
5
|
+
from .parallel import (
|
|
6
|
+
bisect_find_in_chunk,
|
|
7
|
+
chunk_is_sorted,
|
|
8
|
+
linear_find_in_chunk,
|
|
9
|
+
sort_chunk,
|
|
10
|
+
)
|
|
11
|
+
from .sample import reservoir_sample
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"BucketIndex",
|
|
15
|
+
"bisect_find_in_chunk",
|
|
16
|
+
"build_buckets",
|
|
17
|
+
"chunk_is_sorted",
|
|
18
|
+
"external_sort",
|
|
19
|
+
"linear_find_in_chunk",
|
|
20
|
+
"reservoir_sample",
|
|
21
|
+
"sort_chunk",
|
|
22
|
+
]
|