如何链式调用不返回自身的Python原地修改方法?
实现原地方法的链式调用(无需自定义类)
问题背景
Python 中,set() 的 .add() 这类原地方法不会返回修改后的对象,导致无法链式调用:
S = set() S = S.add('item1').add('item2').add('item3')
执行后会报错:
AttributeError: 'NoneType' object has no attribute 'add'
偏好链式调用 .add() 是因为它更贴近自然语言,可读性极强,适合偶尔编程的开发者,方便日后读懂自己的代码。已知可通过自定义类(比如 chainOfSets)实现,但想知道不用自定义类的话,有没有更优的实现方式?
方案1:通用链式包装器(最贴近需求)
写一个通用的包装函数,无需为每种对象类型单独定义类,原地修改原对象的同时支持链式调用:
def chain(obj): class ChainWrapper: def __init__(self, target): self.target = target def __getattr__(self, name): attr = getattr(self.target, name) if callable(attr): def wrapped(*args, **kwargs): result = attr(*args, **kwargs) # 原地方法返回None时,返回包装器自身以支持链式调用;否则返回原结果 return self if result is None else result return wrapped else: return attr def get(self): return self.target return ChainWrapper(obj)
使用方式:
S = set() S = chain(S).add('item1').add('item2').add('item3').get() print(S) # 输出: {'item1', 'item3', 'item2'}
这个包装器是通用的,不仅支持 set,还能用于 list、dict 等其他带原地方法的对象:
lst = chain([]).append(1).append(2).reverse().get() print(lst) # 输出: [2, 1]
方案2:函数式管道写法
如果不想用包装器,可将每个原地操作封装成返回原对象的函数,通过嵌套调用或管道运算符串联:
def set_add(s, item): s.add(item) return s def set_remove(s, item): s.remove(item) return s
使用方式:
# 嵌套调用 S = set() S = set_add(set_add(set_add(S, 'item1'), 'item2'), 'item3') # Python 3.10+ 可用管道运算符简化 S = set() | (lambda s: set_add(s, 'item1')) | (lambda s: set_add(s, 'item2')) | (lambda s: set_add(s, 'item3')) print(S)
方案3:批量操作的简洁写法(适合简单场景)
如果只是批量执行 add 这类单一操作,可通过列表推导式一次性完成,虽不算严格链式调用,但代码简洁:
S = set() _ = [S.add(item) for item in ['item1', 'item2', 'item3']] print(S)
注:_ 是占位变量,仅用于触发列表推导式的执行逻辑。
方案对比
- 通用包装器:最贴近你想要的「点语法链式调用」,通用性强,可读性高,无需重复定义类。
- 函数式管道:无额外类结构,纯函数风格,适合偏好函数式编程的场景。
- 列表推导式:仅适合批量单一操作的简单场景,代码最短但可读性稍弱。
内容的提问来源于stack exchange,提问作者user7711283
相关产品推荐
相关产品推荐

