Python 中的生成器和迭代器

写在前面:

迭代:类似对可迭代对象执行 for…in… 这样操作的过程叫做迭代

可迭代对象

像 Python 的基础类型 list、dict、str 都属性可迭代对象,可以这样来判断一个对象是不是可迭代对象:

from collections.abc import Iterable

if __name__ == "__main__":
    print(isinstance("dolphin", Iterable))
    print(isinstance([1, 2, 3], Iterable))
    print(isinstance({'name': 'dolphin'}, Iterable))

输出结果:

True

True

True

那除了这些基础对象类型之外,我们可以自已定义一个可迭代对象吗?

当然是可以的,可迭代对象的本质是开检查这个对象有没有实现 iter 方法。

自定义可迭代对象

一个对象是否可迭代表现在其是否有实现__iter__方法。所以可以这样来定义一个可迭代对象

from collections.abc import Iterable


class MyList():
    """
    这里只是为展示如何定义可迭代对象,只有简单的这种没有任何意义的
    """
    def __init__(self):
        pass
    
    # 实现 __iter__ 方法
    def __iter__(self):
        pass


if __name__ == "__main__":
    print(isinstance(MyList(), Iterable))

输出结果:

True

如上示例中定义的 MyList 类,虽然使用 isinstance 来验证时已经是可迭代对象,但是因为没有实现 iter 方法,在真正迭代的时候就会出问题。那应该怎么实现 iter 方法呢?

iter 方法需要返回一个迭代器来供进行迭代操作时使用!

迭代器

迭代器是一个可以在迭代过程中记住当前迭代位置的对象。 它从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能前进不会后退。

那怎么自定义一个迭代器呢?

迭代的过程是对迭代器不断的使用 next 方法。

if __name__ == "__main__":
    iterable_obj = [1, 2, 3, 4, 5, 6]

    # 可以通过 iter 方法获取到可迭代对象的迭代器
    iterable_obj_iter = iter(iterable_obj)

    # 使用 next 获取迭代器元素
    iterable_obj_0 = next(iterable_obj_iter)
    iterable_obj_1 = next(iterable_obj_iter)
    iterable_obj_2 = next(iterable_obj_iter)

    print(iterable_obj_0)
    print(iterable_obj_1)
    print(iterable_obj_2)

输出结果:

1
2
3

for…in… 的实质就是不断的调用__next__方法。那这是怎么使用迭代器,到底怎么定义迭代器呢?

当要定义一个迭代器时,只要实现__next__方法他就是一个迭代器了。(但是关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。)

from collections.abc import Iterator


class MyIterator():
    def __next__(self):
        pass

    # 迭代器自身也是可迭代的,返回它自己
    def __iter__(self):
        return self


if __name__ == "__main__":
    my_iterator = MyIterator()
    print(isinstance(my_iterator, Iterator))

输出结果:

True

有状态的迭代器

千万要注意: 迭代器是有状态的,在使用它的时候只可以迭代一次,尽量不要讲迭代器作为函数参数,会引发一些不可知的错误。

生成器

利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一种特殊的迭代器。

列表生成器法生成生成器

把列表生成器的 [] 替换为 () 你将得到一个生成器。

通常在使用列表生成器的时候是这样的

if __name__ == "__main__":
    my_list = [x for x in range(10)]
    print(type(my_list))

输出结果:


但是如果吧列表生成式的 [] 改成 () 将会得到一个生成器。

if __name__ == "__main__":
    # 注意这里 将 [] 换成了 ()
    my_list = (x for x in range(10))
    print(type(my_list))

输出结果:


通过 yield 来生成生成器

def fib(n):
    current = 0
    num1, num2 = 0, 1

    while current < n:
        num = num1
        num1, num2 = num2, num1 + num2
        current += 1
        yield num
    return 'done'


if __name__ == "__main__":
    f = fib(6)
    print(next(f))
    print(next(f))
    print(next(f))
    print(next(f))
    print(next(f))
    print(next(f))

    # 超出
    print(next(f))

输出结果:

0
1
1
2
3
5
Traceback (most recent call last):
File “test.py”, line 24, in
print(next(f))
StopIteration: done

注意: 当迭代生成器时,如果生成器中没有数据了,会抛出 StopIteration 异常,必须要处理它的哟~

你可能感兴趣的:(python)