【从零学习python 】60.探索生成器:迭代的灵活利器

2024-02-29 20:14:12 浏览数 (2)

生成器

1. 生成器

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

2. 创建生成器方法1

要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )

代码语言:javascript复制
L = [x * 2 for x in range(5)]
G = (x * 2 for x in range(5))

创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。我们可以直接打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。

代码语言:javascript复制
next(G) # 输出 0
next(G) # 输出 2
next(G) # 输出 4
next(G) # 输出 6
next(G) # 输出 8
代码语言:javascript复制
G = (x * 2 for x in range(5))
for x in G:
    print(x)

输出结果为:

代码语言:javascript复制
0
2
4
6
8

3. 创建生成器方法2

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

我们仍然用上一节提到的斐波那契数列来举例,回想我们在上一节用迭代器的实现方式:

代码语言:javascript复制
class FibIterator(object):
    """斐波那契数列迭代器"""
    def __init__(self, n):
        """
        :param n: int, 指明生成数列的前n个数
        """
        self.n = n
        # current用来保存当前生成到数列中的第几个数了
        self.current = 0
        # num1用来保存前前一个数,初始值为数列中的第一个数0
        self.num1 = 0
        # num2用来保存前一个数,初始值为数列中的第二个数1
        self.num2 = 1

    def __next__(self):
        """被next()函数调用来获取下一个数"""
        if self.current < self.n:
            num = self.num1
            self.num1, self.num2 = self.num2, self.num1 self.num2
            self.current  = 1
            return num
        else:
            raise StopIteration

    def __iter__(self):
        """迭代器的__iter__返回自身即可"""
        return self

注意,在用迭代器实现的方式中,我们要借助几个变量(n、current、num1、num2)来保存迭代的状态。现在我们用生成器来实现一下。

代码语言:javascript复制
def fib(n):
    current = 0
    num1, num2 = 0, 1
    while current < n:
        yield num1
        num1, num2 = num2, num1 num2
        current  = 1
    return 'done'

4. 使用send唤醒

我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。

例子:执行到yield时,gen函数作用暂时保存,返回i的值; temp接收下次c.send(“python”),send发送过来的值,c.next()等价c.send(None)

代码语言:javascript复制
def gen():
    i = 0
    while i<5:
        temp = yield i
        print(temp)
        i =1

使用send

代码语言:javascript复制
f = gen()
next(f)  # 输出 0
f.send('haha')  # 输出 haha
next(f)  # 输出 None
f.send('haha')  # 输出 haha

使用next函数

代码语言:javascript复制
f = gen()
next(f)  # 输出 0
next(f)  # 输出 None
next(f)  # 输出 None
next(f)  # 输出 None
next(f)  # 输出 None
next(f)  # 抛出 StopIteration 异常

使用__next__()方法(不常使用)

代码语言:javascript复制
f = gen()
f.__next__()  # 输出 0
f.__next__()  # 输出 None
f.__next__()  # 输出 None
f.__next__()  # 输出 None
f.__next__()  # 输出 None
f.__next__()  # 抛出 StopIteration 异常

以上就是生成器的基本用法。生成器在迭代过程中可以暂停和继续,非常灵活,适合处理大量的数据或者需要延迟生成的数据。

0 人点赞