如何重写Python列表的FOR IN循环行为?
问题描述
我尝试实现一个列表类,使其在通过索引直接访问或使用for in循环语句时,返回经过包装的元素。目前编写了如下代码:
class ItemWrappedList(list): def __getitem__(self, y): result = super().__getitem__(y) return f'<item>{result}</item>' lst = ItemWrappedList(['1', '2', '3']) for pos in range(len(lst)): print(lst[pos]) #>>> <item>1</item> #>>> <item>2</item> #>>> <item>3</item> for value in lst: print(value) #>>> 1 #>>> 2 #>>> 3
如你所见,第一个循环的运行符合预期,但第二个循环调用了我无法重写的列表方法。有没有办法让ItemWrappedList类在所有场景下保持一致的行为?另外,我尝试过多种方式实现/重写__next__和__iter__方法,但似乎__next__从未被调用。
解决方案
问题核心在于for in循环的底层逻辑:Python的for循环会调用对象的__iter__方法获取迭代器,而默认的list迭代器直接读取内部原始元素,不会经过你重写的__getitem__。你之前重写__next__没生效,是因为如果__iter__返回的是父类list的迭代器,那__next__是这个迭代器对象的方法,而非你的ItemWrappedList类的方法,自然不会被触发。
要让两种访问方式行为一致,只需重写__iter__方法,让它返回能生成包装后元素的迭代器即可,以下是两种实用实现方式:
方式一:用生成器快速实现
class ItemWrappedList(list): def __getitem__(self, y): result = super().__getitem__(y) return f'<item>{result}</item>' def __iter__(self): # 遍历父类迭代器,对每个元素做包装后返回 for item in super().__iter__(): yield f'<item>{item}</item>'
方式二:自定义迭代器类(适合复杂逻辑)
如果需要更灵活的迭代控制,可以单独实现迭代器类:
class ItemWrappedList(list): def __getitem__(self, y): result = super().__getitem__(y) return f'<item>{result}</item>' def __iter__(self): return WrappedIterator(self) class WrappedIterator: def __init__(self, wrapped_list): self.index = 0 self.wrapped_list = wrapped_list def __next__(self): if self.index >= len(self.wrapped_list): raise StopIteration # 直接调用列表的__getitem__,自动复用包装逻辑 item = self.wrapped_list[self.index] self.index += 1 return item
两种方式都能让for in循环返回包装后的元素,同时保留索引访问的原有行为。测试效果:
lst = ItemWrappedList(['1', '2', '3']) for value in lst: print(value) #>>> <item>1</item> #>>> <item>2</item> #>>> <item>3</item>
内容的提问来源于stack exchange,提问作者Yuri
相关产品推荐
相关产品推荐

