
本文介绍如何设计一个通用的 Python 集合类,使其能透明地将属性访问和方法调用自动分发到内部所有元素上,并支持通过 apply() 显式应用任意外部函数,从而实现类似 NumPy 的向量化语义。
本文介绍如何设计一个通用的 python 集合类,使其能透明地将属性访问和方法调用自动分发到内部所有元素上,并支持通过 `apply()` 显式应用任意外部函数,从而实现类似 numpy 的向量化语义。
在面向对象编程中,我们常希望对一组同构对象(如多个 A 实例)进行“批量操作”,即让集合对象 A_Collection 表现得像一个“放大版”的 A:访问 ac.attr 返回 [a1.attr, a2.attr],调用 ac.method() 返回 [a1.method(), a2.method()],甚至支持 np.array(ac) 等第三方函数的合理处理。然而,Python 的 __getattr__ 仅能拦截属性/方法访问,无法控制外部函数传入集合作为参数时的行为(如 np.array(ac)),因为此时控制权完全交由 np.array,集合类无从介入。
因此,一个稳健的设计需遵循两个原则:
✅ 代理(Proxy):通过 __getattr__ 自动转发已知属性与可调用成员;
✅ 显式应用(Apply):提供 apply(func) 方法,将任意函数(包括 np.array, str, len 等)统一作用于每个元素,并返回新集合。
以下是一个生产就绪的参考实现,基于 collections.abc.Sequence 构建,兼顾协议兼容性与行为一致性:
from collections.abc import Sequence
class ApplierSequence(Sequence):
def __init__(self, initial):
self.data = list(initial) # 强制转为列表,确保可索引
def apply(self, function):
"""对每个元素应用 function,返回同类型新实例"""
return type(self)(function(item) for item in self.data)
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return len(self.data)
def __getattr__(self, attr):
# 获取每个元素的 attr 值
values = [getattr(item, attr) for item in self.data]
# 若首个值是可调用对象(方法),则批量调用并收集结果
if values and callable(values[0]):
return type(self)(value() for value in values)
return type(self)(values)
def __repr__(self):
return f"{self.__class__.__name__}({self.data!r})"
使用示例:
class A:
def __init__(self, i):
self.i = i
def add_one(self):
return self.i + 1
def __str__(self):
return f"A({self.i})"
# 构建集合
ac = ApplierSequence([A(5), A(6), A(7)])
print(ac.i) # ApplierSequence([5, 6, 7])
print(ac.add_one()) # ApplierSequence([6, 7, 8])
print(ac.__str__()) # ApplierSequence(['A(5)', 'A(6)', 'A(7)'])
# 使用 apply 处理外部函数
import numpy as np
arrays = ac.apply(np.array) # → ApplierSequence([array(5), array(6), array(7)])
strings = ac.apply(str) # → ApplierSequence(['A(5)', 'A(6)', 'A(7)'])
⚠️ 注意事项:
-
__getattr__不会拦截__len__、__iter__等特殊方法,务必显式实现或委托给self.data; - 对于返回标量的属性(如
ac.i是列表),若后续需数值运算,应额外实现__array__(用于 NumPy)、__add__等魔法方法; -
apply()是安全、明确且可组合的操作,推荐优先使用它替代隐式代理,尤其当函数行为不确定时; - 若需深度集成(如支持
ac + 1或ac.mean()),建议继承numpy.lib.mixins.NDArrayOperatorsMixin并重载运算符。
该模式在科学计算、配置管理、测试数据构造等场景中极具价值——它既保持了底层对象的语义完整性,又赋予集合以“向量化”表达力,是 Python 动态特性的优雅实践。










