解析、迭代和生成系列文章:https://www.cnblogs.com/f-ck-need-u/p/9832640.html
<https://www.cnblogs.com/f-ck-need-u/p/9832640.html#blogiterator>

本文介绍如何自定义迭代器,涉及到类的运算符重载,包括__getitem__的索引迭代,以及__iter__、__next__和__contains__
,如果不了解这些知识可跳过本文。

索引迭代方式

索引取值和分片取值

元组、列表、字典、集合、字符串都支持索引取值操作和分片操作。
>>> L = [11,21,31,41] >>> L[0] 11 >>> L[0:2] [11, 21]
分片操作实际上将一个slice对象当作索引位传递给序列,然后以索引取值的方式取得所需元素。
>>> L[0:2] [11, 21] >>> L[slice(0,2)] [11, 21]
slice对象由slice()函数创建,它有3个参数:起始索引位、结束索引位、步进值。例如:
>>> slice(0,2) slice(0, 2, None)
__getitem__

列表、元组等序列之所以可以索引取值、分片取值,是因为它们实现了__getitem__方法。

例如:
>>> hasattr(list,"__getitem__") True >>> hasattr(tuple,"__getitem__") True >>>
hasattr(dict,"__getitem__") True >>> hasattr(str,"__getitem__") True
如果自定义类并实现__getitem__方法,它们会重载索引取值:
class cls: def __getitem__(self, index): print("getitem index", index) return
index * 2 >>> c = cls() >>> c[1] getitem index 1 2 >>> c[2] getitem index 2 4
>>> c[3] getitem index 3 6
上面的自定义类只支持索引取值,不支持分片取值。因为__getitem__中没有编写索引取值的方式,也就不支持传递slice对象来进行分片取值。

分片和__getitem__

如果想要__getitem__支持分片取值,需要在__getitem__中使用索引取值的方式,以便支持slice对象作为索引。

下面是一个简单的支持分片操作的自定义类:
class cls: def __init__(self,data): self._data = data def
__getitem__(self,index): print("getitem:",index) return self._data[index] >>> c
= cls([1,2,3,4]) >>> c[1] getitem: 1 2 >>> c[0:2] getitem: slice(0, 2, None)
[1, 2]
__setitem__和__delitem__

如果想要索引或者分片赋值,那么会调用__setitem__()方法,如果想要删除索引值或分片值,会调用__delitem__()方法。
class cls: def __init__(self,data): self._data = data def
__getitem__(self,index): print("in getitem") return self._data[index] def
__setitem__(self,index,value): print("in setitem") self._data[index] = value
def __delitem__(self,index): print("in delitem") del self._data[index] def
__repr__(self): return str(self._data) >>> c = cls([11,22,33,44,55]) >>> c[1:3]
in getitem [22, 33] >>> c[1:3] = [222,333] in setitem >>> c [11, 222, 333, 44,
55] >>> del c[1:3] in delitem
__getitem__索引迭代

__getitem__
重载了索引取值和分片操作,实际上它也能重载索引的迭代操作。以for为例,它会循环获取一个个的索引并向后偏移,直到超出索引边界抛出IndexError异常而停止。

此外,__getitem__
重载使得它可以被迭代,也就是它通过数值索引的方式让这个对象变成可迭代对象,所有迭代工具(比如zip/map/for/in)都可以对这个对象进行迭代操作。
class cls: def __init__(self,data): self._data = data def
__getitem__(self,index): return self._data[index] def __repr__(self): return
str(self._data) >>> c1 = cls([11,22,33,44,55]) >>> I = iter(c1) >>> next(I) 11
>>> 22 in I True >>> I=iter(c1) >>> for i in I:print(i,end=" ") ... 11 22 33 44
55
可迭代对象:__iter__和__next__

定以了__getitem__的类是可迭代的类型,是通过数值索引的方式进行迭代的,但这是退而求其次的行为,更好的方式是定义__iter__
方法,使用迭代协议进行迭代。当同时定义了__iter__和__getitem__的时候,iter()函数优先选择__iter__,只有在__iter__
不存在的时候才会选择__getitem__。

例如:
class Squares: def __init__(self, start, stop): # 迭代起始、终止位 self.value = start
self.stop = stop def __iter__(self): # 返回自身的迭代器 return self def __next__(self):
# 返回下一个元素 if self.value > self.stop: # 结尾时抛出异常 raise (StopIteration) item =
self.value**2 self.value += 1 return item if __name__ == "__main__": for i in
Squares(1, 5): print(i, end=" ") s = Squares(1,5) print() print(9 in s)
运行结果:
1 4 9 16 25 True
因为上面的类中同时定义了__iter__和__next__,且__iter__返回的是自身,所以这个类型的每个迭代对象都是单迭代的。
>>> s = Squares(1,5) >>> I1 = iter(s) # I1和I2迭代的是同一个对象 >>> I2 = iter(s) >>>
next(I1) 1 >>> next(I2) # 继续从前面的位置迭代 4 >>> next(I1) 9
自定义多迭代类型

要定义多迭代的类型,要求__iter__返回一个新的迭代对象,而不是self自身,也就是说不要返回自身的迭代器。

例如:
# 返回多个独立的可迭代对象 class MultiIterator: def __init__(self, wrapped): self.wrapped
= wrapped # 封装将被迭代的对象 def __iter__(self): return Next(self.wrapped) #
返回独立的可迭代对象 # 自身的迭代器 class Next: def __init__(self, wrapped): self.wrapped =
wrapped self.offset = 0 def __iter__(self): return self def __next__(self): #
返回下一个元素 if self.offset >= len(self.wrapped): raise (StopIteration) else: item =
self.wrapped[self.offset] self.offset += 1 return item # 返回指定索引位置处的元素 if
__name__ == "__main__": string = "abc" s = MultiIterator(string) for x in s:
for y in s: print(x + y, end=" ")
每个for迭代工具都会先调用iter()来获取可迭代对象,然后调用next()获取下一个元素。而这里的iter()会调用MultiIterator的
__iter__来获取可迭代对象,而MultiIterator所返回的可迭代对象是相互独立的Next对象,因此for x in x和for y in s
所迭代的是不同迭代对象,它们都有记录着自己的迭代位置信息。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信