本文介紹如何自定義迭代器,涉及到類的運算符重載,包括 的索引迭代,以及 、`__next__ __contains__`,如果不瞭解這些知識可跳過本文。 索引迭代方式 索引取值和分片取值 元組、列表、字典、集合、字元串都支持索引取值操作和分片操作。 分片操作實際上將一個slice對象當作索引位傳遞給 ...
解析、迭代和生成系列文章:https://www.cnblogs.com/f-ck-need-u/p/9832640.html
本文介紹如何自定義迭代器,涉及到類的運算符重載,包括__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
所迭代的是不同迭代對象,它們都有記錄著自己的迭代位置信息。