如果一个函数返回的是迭代器,那么这个函数就被称为生成器(generator),一个函数如果使用yield关键字返回或接收值,那么这个函数就是生成器。
def gen(): yield 1 a = gen() print(a.__next__()) print(type(a)) ----------------------------------- 1 <class 'generator'>
使用列表生成式会生成一个list,但将列表生成式的[]替换成()后,返回的就是generator
>>> type([ x for x in range(5)]) <class 'list'> >>> type((x for x in range(5))) <class 'generator'>
生成器对象也是一个迭代器对象,可以使用__next__()函数来迭代数据
>>> gen = (x for x in range(5))>>> gen.__next__() 0 >>> gen.__next__() 1 >>> gen.__next__() 2
我们分别使用return关键字和yield关键字看一下两者的区别
def gen(): print("这是一行输出") return 1 #yield 1 a = gen() print("a=",a)
-------------------------
这是一行输出
a= 1
def gen(): print("这是一行输出") #return 1 yield 1 a = gen() print("a=",a) ---------------------------------- a= <generator object gen at 0x01194F00>
可以看到,当用一个变量接收一个一个函数时,会将函数的返回值赋给变量,并且会执行整个函数,而使用yield关键字时,函数的内部不会执行,并且返回的是一个生成器,需要使用__next__()方法才能将yield后的值返回出来,再例如:
>>> def gen(): ... print("这是第一行输出") ... yield 1 ... print("这是第二行输出") ... yield 2 ... >>> a = gen() >>> a.__next__() 这是第一行输出 1 >>> a.__next__() 这是第二行输出 2
可以看到,当使用变量接收一个生成器时,与函数不同,生成器里的内容都不会被执行,只有使用__next()__后,生成器才会开始执行,并在遇到yield时停止,当函数已经执行完后使用__next__()函数就会报出StopIteration的异常,我们接上面的代码执行__next__()函数,如下:
>>> a.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
生成器的作用就是为了在数据量比较大的情况节省空间,一般是在生成大量数据时使用,所以一般会与where循环一起使用,一种常用的生成器的定义方式应该是:
def num(): n = 0 while True: yield n n +=1 if n > 10: break i = num() while True: try: print(i.__next__()) except StopIteration: break
如迭代器一样,遍历生成器时可以使用try...except...来接收StopIteration异常,当然也可以将生成器对象作为普通的迭代器对象来使用for循环遍历:
def num(): n = 0 while True: yield n n +=1 if n > 10: break i = num() for a in i: print(a)
两种方式输出的结果一致。
生成器中除了可以使用__next__()方法获取生成器生成的数据外,还可以使用send()方法像生成器中传值:
def sum(): n = 0 while True: a = yield n print("a = ",a) n = a+n s = sum() print(s.__next__()) print(s.send(2))
-------------------------------
0
a = 2
2
__next__()函数等同于send(None),生成器中所有使用__next__()的地方都可以使用send(None)代替,生成器第一次执行时是从函数开始的地方执行,因为没有值获取yield传来的值,所以第一次执行时必须使用__next__()或者参数是‘None’的send()方法。从上面的输出可以看出执行send()方法时先执行 "a = yield"然后执行其他代码,最后才会执行"yiled n"然后结束
只需要熟练的运用__next__()和send()方法,了解yield的机制,对于生成器的使用就不会有什么问题了