如何为itertools.chain对象设置__len__方法?
解决itertools.chain实例无法添加__len__的问题
你遇到的问题根源在于:itertools.chain是C语言实现的内置类,这类对象的实例不支持动态添加属性,所以直接用setattr给实例加__len__方法会失败。
下面给你几个实用的解决方案,满足tqdm显示进度条的需求:
方案1:直接给tqdm指定total参数(最简便)
tqdm本身支持手动传入total参数,不需要修改chain对象,这是最省事的做法:
from itertools import chain from tqdm import tqdm list_1 = list(range(5, 15)) list_2 = list(range(20, 30)) chained = chain(list_1, list_2) # 直接把总长度传给tqdm的total参数 for item in tqdm(chained, total=len(list_1) + len(list_2)): # 这里写你的处理逻辑 pass
方案2:自定义带__len__的包装类
如果需要让chain对象本身支持len()调用,可以写一个简单的包装类,把原chain对象包起来:
from itertools import chain from tqdm import tqdm class ChainWithLen: def __init__(self, *iterables): self._chain = chain(*iterables) # 计算所有可迭代对象的总长度(仅适用于有__len__的对象,比如列表) self._total_len = sum(len(it) for it in iterables if hasattr(it, '__len__')) def __iter__(self): # 让包装类支持迭代,直接返回原chain的迭代器 return iter(self._chain) def __len__(self): return self._total_len # 使用示例 list_1 = list(range(5, 15)) list_2 = list(range(20, 30)) chained = ChainWithLen(list_1, list_2) print(len(chained)) # 输出20 # 直接用tqdm,不需要额外传total for item in tqdm(chained): # 处理逻辑 pass
方案3:通过子类扩展chain(不推荐,偏hack)
如果一定要基于原chain类扩展,可以创建子类,但因为chain是C实现类,子类需要手动处理初始化:
from itertools import chain from tqdm import tqdm class ChainWithLen(chain): def __init__(self, *iterables): super().__init__(*iterables) self._total_len = sum(len(it) for it in iterables if hasattr(it, '__len__')) def __len__(self): return self._total_len # 使用示例 list_1 = list(range(5, 15)) list_2 = list(range(20, 30)) chained = ChainWithLen(list_1, list_2) for item in tqdm(chained): # 处理逻辑 pass
注意:这个方案依赖Python对C实现类的子类化支持,不同版本可能有兼容性问题,优先推荐前两个方案。
内容的提问来源于stack exchange,提问作者DSantiagoBC
相关产品推荐
相关产品推荐

