python哪些类型可以作为迭代器_Python迭代和解析(4):自定义迭代器
发布日期:2021-10-31 18:34:52 浏览次数:33 分类:技术文章

本文共 3863 字,大约阅读时间需要 12 分钟。

本文介绍如何自定义迭代器,涉及到类的运算符重载,包括__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所迭代的是不同迭代对象,它们都有记录着自己的迭代位置信息。

转载地址:https://blog.csdn.net/weixin_40006779/article/details/110766234 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:python链表赋值_Python链表
下一篇:python不用声明变量吗_python不用声明变量吗

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年03月29日 06时24分45秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章