python中迭代器的原理与使用

北城余情 提交于 2020-01-25 01:14:29

python中,一个类只要实现了__iter__()方法,则称其为可迭代的,如果其又实现了next()方法(在python3中为__next__()方法,本文中不再进行说明),则可称其为迭代器。其中,__iter()__方法的用途是返回一个迭代器,并通过返回的迭代器的next()来迭代所需的容器。

迭代器的用处,主要是用来遍历一个我们需要的容器,如我们常见的for in循环,通过对于我们自己写的类添加迭代的方法,便可以使得我们的自定义的类可以像内置的列表一样,方便地通过for in来直接遍历。

以下是一个比较简单的迭代器的使用方法。

首先,假设我们有一个类MyArray,其中有一个字符串的列表self.elements

class MyArray(object):
    def __init__(self):
        self.elements = ["e1", "e2", "e3"]
        self.len = len(self.elements)

这时,我们知道,我们是无法直接遍历MyArray的,只能通过访问其中的元素来实现。这时,我们可以通过一种简单的方法来使得MyArray变成一个可迭代的类。

class MyArray(object):
    def __init__(self):
        self.elements = ["e1", "e2", "e3"]
        self.len = len(self.elements)
        self.pos = 0

    def __iter__(self):
        # 因为自身也为一个迭代器,因此直接返回自身即可
        return self

    def next(self):
        # StopIteration是以抛出异常的方式说明已经遍历完目标容器
        if self.pos >= self.len:
            raise StopIteration()
        self.pos += 1
        return self.elements[self.pos - 1]

测试代码:

if __name__ == '__main__':
    my_array = MyArray()
    iterator = iter(my_array)
    print next(iterator)
    print next(iterator)
    print next(iterator)
    print next(iterator)

结果:

e1
e2
e3
Traceback (most recent call last):
  File "/home/lenfranky/codes/home/lenfranky/codes/DensityClustering/codes/testCodes/iterTest/MyArray.py", line 32, in <module>
    print next(iterator)
  File "/home/lenfranky/codes/home/lenfranky/codes/DensityClustering/codes/testCodes/iterTest/MyArray.py", line 13, in next
    raise StopIteration()
StopIteration

在我们的第一种写法中,定义了一个自定义的数组类MyArray,并且将其作为目标类来遍历。而迭代器的设置方法为将该类直接设置为一个迭代器,即,在类MyArray中,定义了__iter__()方法与next(self)方法,使得该类本身成为了一个迭代器。

通过这样的写法,只需要一个类即可完成所有的工作,看起来更简单一些,在一些特定的场合也可这样使用,但是会存在着一些问题。这样的迭代器是可以像列表一样用for来进行遍历的,但是由于self.pos是没有重置的,因此只能遍历一次。测试代码如下:

if __name__ == '__main__':
    my_array = MyArray()
    for ch in my_array:
        print ch
    for ch in my_array:
        print ch

结果:

e1
e2
e3

从结果可以看到,在遍历一次之后,第二次用for进行遍历,便无法正确地输出结果,只能输出一次。当然,对于这样的写法,如此的问题是可以通过在抛出异常语句raise StopIteration()之前,添加初始化位置坐标的语句self.pos = 0来弥补这个缺点,实现多次for的。但是,一个更加常见的方法是,定义一个专门的迭代器的类来实现这些操作,使得我们的数据类,或者说目标类,MyArray中,可以尽量少地包含这些代码,更加专注于其本应的工作,而迭代器相关的操作可以留在迭代器的类中来写。

class MyArray(object):
    def __init__(self):
        self.elements = ["e1", "e2", "e3"]
        self.len = len(self.elements)

    def __iter__(self):
        return MyArrayIterator(self)

class MyArrayIterator(object):
    def __init__(self, array):
        self.elements = array.elements
        self.len = len(self.elements)
        self.pos = 0

    def __iter__(self):
        return self

    def next(self):
        if self.pos >= self.len:
            raise StopIteration()
        self.pos += 1
        return self.elements[self.pos - 1]

if __name__ == '__main__':
    my_array = MyArray()
    for ch in my_array:
        print ch
    for ch in my_array:
        print ch
e1
e2
e3
e1
e2
e3

在上面的代码中,定义了一个专门的迭代器的类MyArrayIterator,并在其中的next()中实现了正序遍历的功能,之后,在MyArray中写了__iter__()方法,说明MyArray是可迭代的,并且在这个方法中返回我们刚刚所写的迭代器MyArrayIterator的实例,通过这个实例来实现遍历的功能。

像这样利用专门的迭代器的类来实现迭代器的功能相比于把迭代器的代码直接写在目标类中,有着很多的优势。一个很明显的优点是,这样的写法非常地方便,扩展性与可修改性更好,因为迭代器相关的代码是在另一个类中,因此,如果我们想要另一种迭代的方式,并不需要直接去修改目标类,只需要去修改迭代器的类即可。或者,当我们想要一种新的迭代方法时,我们可以直接写一个新的迭代器的类,来实现这种迭代方法,之后,直接实例化新的迭代器的类来进行遍历即可。

下面,便是一个倒序遍历我们的MyArray的迭代器的例子。

class MyArrayIterator(object):
    def __init__(self, array):
        self.elements = array.elements
        self.len = len(self.elements)
        self.pos = 0

    def __iter__(self):
        return self

    def next(self):
        if self.pos >= self.len:
            raise StopIteration()
        self.pos += 1
        return self.elements[self.pos - 1]


class MyArrayReverseIterator(object):
    def __init__(self, array):
        self.elements = array.elements
        self.len = len(self.elements)
        self.pos = 0

    def __iter__(self):
        return self

    def next(self):
        if self.pos >= self.len:
            raise StopIteration()
        self.pos += 1
        return self.elements[self.len - self.pos]


if __name__ == '__main__':
    my_array = MyArray()
    my_iterator = MyArrayIterator(my_array)
    for ch in my_iterator:
        print ch

    my_reverse_iterator = MyArrayReverseIterator(my_array)
    for ch in my_reverse_iterator:
        print ch
e1
e2
e3
e3
e2
e1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!