生成器
1. 生成器
利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。生成器是一类特殊的迭代器。
2. 创建生成器方法1
要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
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()等方法使用。
next(G) # 输出 0 next(G) # 输出 2 next(G) # 输出 4 next(G) # 输出 6 next(G) # 输出 8
G = (x * 2 for x in range(5)) for x in G: print(x)
输出结果为:
0 2 4 6 8
3. 创建生成器方法2
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
我们仍然用上一节提到的斐波那契数列来举例,回想我们在上一节用迭代器的实现方式:
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)来保存迭代的状态。现在我们用生成器来实现一下。
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)
def gen(): i = 0 while i<5: temp = yield i print(temp) i+=1
使用send
f = gen() next(f) # 输出 0 f.send('haha') # 输出 haha next(f) # 输出 None f.send('haha') # 输出 haha
使用next函数
f = gen() next(f) # 输出 0 next(f) # 输出 None next(f) # 输出 None next(f) # 输出 None next(f) # 输出 None next(f) # 抛出 StopIteration 异常
使用__next__()方法(不常使用)
f = gen() f.__next__() # 输出 0 f.__next__() # 输出 None f.__next__() # 输出 None f.__next__() # 输出 None f.__next__() # 输出 None f.__next__() # 抛出 StopIteration 异常
以上就是生成器的基本用法。生成器在迭代过程中可以暂停和继续,非常灵活,适合处理大量的数据或者需要延迟生成的数据。