Python基础教程:自定义迭代器

本文介绍如何自定义迭代器,涉及到类的运算符重载,包括__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__方法,它们会重载索引取值:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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__()方法。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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__

例如:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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所迭代的是不同迭代对象,它们都有记录着自己的迭代位置信息。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 自定义迭代器是指通过编写自定义的类和方法,实现对对象的迭代访问和遍历。在这个过程中,我们可以使用 __iter__ 和 __next__ 这两个特殊的方法来实现迭代器的定义。 首先,我们需要创建一个自定义的类,并在这个类中定义 __iter__ 和 __next__ 这两个方法。在 __iter__ 方法中,我们可以对迭代器进行初始化,并返回一个可迭代对象。在 __next__ 方法中,我们可以定义迭代器的遍历逻辑,并返回下一个元素。 以一个示例来说明,假设我们要创建一个自定义迭代器来遍历一个列表中的元素,我们可以这样实现: ```python class MyIterator: def __init__(self, data): self.data = data self.index = 0 def __iter__(self): return self def __next__(self): if self.index < len(self.data): value = self.data[self.index] self.index += 1 return value else: raise StopIteration ``` 通过上面的代码,我们创建了一个名为 MyIterator 的自定义迭代器类,并实现了 __iter__ 和 __next__ 方法。在 __init__ 方法中,我们对迭代器进行了初始化,并指定了需要遍历的列表数据。在 __next__ 方法中,我们定义了迭代器的遍历逻辑,并实现了对列表中元素的遍历。 最后,我们可以通过使用 for 循环或者调用 next() 方法来使用这个自定义迭代器,例如: ```python data = [1, 2, 3, 4, 5] my_iterator = MyIterator(data) for value in my_iterator: print(value) ``` 以上就是关于 Python 自定义迭代器的一些基本介绍和示例,希望可以帮助到你。如果想要深入了解更多内容,可以参考 CSDN 等相关平台上的文档和教程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值