Python迭代器和生成器

1,831 阅读4分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

在Python中,迭代器、生成器毫无疑问是最有用的特性之一,功能也非常强大,在Python中经常出现,但是常常被我们忽视,或者说不太理解它们的原理机制,本文就围绕迭代器、生成器、可迭代对象这些来详细阐述一下它们之间的关系以及它们的强大之处。

可迭代对象

说到迭代器和生成器,一定绕不过去的点就是可迭代对象,那么什么是可迭代对象呢,最简单的解释就是:可以使用for···in···语句进行循环的对象,就是可迭代对象,像我们熟知的字符串、列表、元组、字典等都属于可迭代对象。

那么如何判断一个对象是否属于可迭代对象呢?我们可以使用isinstance()来判断。

from collections.abc import Iterable

print(isinstance([], Iterable))
print(isinstance([1, 2, 3, 4], Iterable))
print(isinstance({"name": "tigeriaf"}, Iterable))
print(isinstance("tigeriaf", Iterable))
print(isinstance((1, 2, 3, 4), Iterable))

因为它们都是可迭代对象,所以结果都为True。

还有重要的一点是:可迭代对象需要实现__iter__方法,也就是说实现了__iter__()方法的对象,就是可迭代对象。例如下面这个MyIter类实现了__iter__()方法,那么它的对象a就是一个可迭代对象。

class MyIter:
    def __init__(self):
        pass

    def __iter__(self):
        yield self  # __iter__()需要返回一个迭代器


a = MyIter()


print(isinstance(a, Iterable))
for i in a:
    print(i)

结果为:

True
<__main__.MyIter object at 0x01A9FEB0>

Process finished with exit code 0

迭代器Iterator

迭代器是可迭代对象的一个子集,它可以记住遍历的位置,它与列表、元组、集合、字符串这些可迭代对象的区别就在于next()方法,迭代器可以通过next()方法不断的获取下一个值,直到所有元素全部输出完之后,返回StopIteration异常才会停止。也就是说迭代器不仅要实现__iter__()方法,还需要实现__next__()方法。

例如下面一段代码,通过实现了__iter__()方法和__next__()方法自定义了一个迭代器。

class MyRange:
    def __init__(self, stop, start=0):
        self.start = start
        self.stop = stop

    def __iter__(self):
        # 因为实现了__next__(),所以自身就是一个迭代器,这里就可以直接使用return返回自己
        return self

    def __next__(self):
        if self.start < self.stop:
            res = self.start
            self.start += 1
        else:
            raise StopIteration
        return res


print(isinstance(MyRange(3), Iterable))
print(isinstance(MyRange(3), Iterator))

for i in MyRange(3):
    print(i)

结果为:

True
True
0
1
2

Process finished with exit code 0

其实列表、元组、集合、字符串这些可迭代对象也可以使用iter()函数转化为迭代器,下面来看一个例子,

a = [1, 2, 3, 4, 5]
print(isinstance(a, Iterator))
b = iter(a)
print(isinstance(b, Iterator))
print(next(b))
print(next(b))

输出为:

False
True
1
2

Process finished with exit code 0

通过结果可见,通过iter函数把a转化成了迭代器,从而能够通过next()方法不断从前往后遍历。

生成器Generator

上面我们说迭代器是可迭代对象的一个子集,而生成器则是迭代器的子集,实现生成器使用的是关键字yiled,在Python中一个函数可以用yiled替代return返回值,这样的话这个函数就变成了一个生成器对象。 它和迭代器的区别是,生成器并不是一开始就把所有值装载进内存,只是在需要使用next()函数获取值的时候,才会取一个值返回,这样一来,在数量量非常大的时候不会占用大量的内存。

def func(end):
    start = 0
    while start < end:
        yield start
        start += 1

print(isinstance(func(4), Generator))

结果为True

生成器表达式

生成器表达式是一种实现生成器的便捷方式,将列表推导式的中括号替换为圆括号。和列表推导式的区别:列表生成式是一开始直接创建一个列表,但是生成器表达式是一种边循环边计算的形式,使得列表的元素可以在循环过程中一个个的推算出来,不需要创建完整的列表,从而节省了大量的空间。

g = (x * x for x in range(100))

总结

  • 实现了__iter__()方法的对象为可迭代对象(Iterable),常见的可迭代对象有列表、元组、集合、字符串等;
  • 实现了__iter__()__next__()方法的为迭代器(Iterator);
  • 函数中应用yield关键字的对象为生成器(Generator),生成器也是迭代器和可迭代对象。
  • 迭代器Iterator、生成器Generator都需要通过for循环,或者手动调用next()来逐个获取其内部值。
  • 迭代器、生成器的使用可以节省内存,但前提是我们能够按照某种算法推算出来,在循环的过程中不断推算出后续想要的元素。 放一个大佬的图,链接

image.png

最后,感谢女朋友在工作和生活中的包容、理解与支持 !