把一个 OperatorABC 实例包装成按 batch_size 批处理的 Wrapper 同时让 wrapper.run 的函数签名 = operator.run 的签名
(operator: OperatorABC, batch_size: int = 32)
| 4 | from dataflow.core import WrapperABC, OperatorABC |
| 5 | |
| 6 | def BatchWrapper(operator: OperatorABC, batch_size: int = 32) -> WrapperABC: |
| 7 | """ |
| 8 | 把一个 OperatorABC 实例包装成按 batch_size 批处理的 Wrapper |
| 9 | 同时让 wrapper.run 的函数签名 = operator.run 的签名 |
| 10 | """ |
| 11 | logger = get_logger() |
| 12 | logger.info(f"Creating BatchWrapper for {operator.__class__.__name__} with batch size {batch_size}") |
| 13 | |
| 14 | # 1) 先取得被包装 operator.run 的签名 |
| 15 | original_run = operator.run |
| 16 | sig = inspect.signature(original_run) |
| 17 | |
| 18 | # 2) 动态定义一个 run 方法 |
| 19 | def run(self, *args, **kwargs): |
| 20 | """ |
| 21 | 这个文档和签名都会被下面的 __wrapped__ / __signature__ 覆盖成 operator.run 的 |
| 22 | """ |
| 23 | # 假设 operator.run 返回一个可迭代的数据项流 |
| 24 | it = self._operator.run(*args, **kwargs) |
| 25 | batch = [] |
| 26 | for item in it: |
| 27 | batch.append(item) |
| 28 | if len(batch) >= self._batch_size: |
| 29 | # 交给原 operator 处理这一小批 |
| 30 | self._operator.process(batch) |
| 31 | batch = [] |
| 32 | # 处理最后不满 batch_size 的残余 |
| 33 | if batch: |
| 34 | self._operator.process(batch) |
| 35 | |
| 36 | # 3) 把 operator.run 本身记录到 __wrapped__(便于追踪) |
| 37 | run.__wrapped__ = original_run |
| 38 | # 4) 把签名贴到 run.__signature__ 上(inspect.signature/run-time 补全会拿到它) |
| 39 | run.__signature__ = sig |
| 40 | |
| 41 | # 5) 动态创建一个新的 Wrapper 子类,并把这个 run 方法挂上去 |
| 42 | BatchWrapperImpl = type( |
| 43 | f"BatchWrapper_{operator.__class__.__name__}", |
| 44 | (WrapperABC,), |
| 45 | { |
| 46 | "__init__": lambda self: ( |
| 47 | setattr(self, "_operator", operator), |
| 48 | setattr(self, "_batch_size", batch_size) |
| 49 | ), |
| 50 | "run": run |
| 51 | } |
| 52 | ) |
| 53 | |
| 54 | # 6) 返回实例 |
| 55 | return BatchWrapperImpl() |
| 56 | |
| 57 | |
| 58 | # 测试示例 |
no test coverage detected