在python中,边循环边计算的机制就称为生成器(generator)。使用生成器当用到数据的时候在生成,这样可以节约空间,提高效率。生成器是一种特殊类型的迭代器,所以也会有__init__()和__next__()两个方法:
__iter__方法负责返回一个迭代器(在迭代器中返回自己,在可迭代对象中返回帮助自己迭代的迭代器)
_next__方法:当前要获取的元素没有越界,就返回当前元素,然后自己指向为下一个元素等待返回;如果上次反回了最后一个元素,这一次再调用next的时候已经没有元素了,就抛出StopIteration异常。
myIterator = ( x*2 for x in range(5))
print(type(myIterator))
print(next(myIterator))
print(next(myIterator))
print(next(myIterator))
print(next(myIterator))
print(next(myIterator))
print(next(myIterator))#报错,因为没有元素可以取了
执行结果:
说明:
写法和推导列表非常相似,只是由[]换成了()。由于生成器也是迭代器,所以可以使用next()方法将元素一个一个取出来,当所有的元素都取出后再调用next()方法就会抛出StopIteration异常。
myIterator = ( x*2 for x in range(11))
print(type(myIterator))
for i in myIterator:
print(i,end=" ")
执行结果:
说明:
由于生成器也是迭代器所以可以放在for循环中操作,将元素一个一个取出来。可以发现使用这种方法不会有异常抛出,因为for循环内部已经处理了。
比如我们定义一个函数,打印生成n以内的整数,代码如下:
def my_print(n):
for i in range(n):
print(i)
my_print(5)
执行结果:
当把print换成yield之后,这个函数就是一个生成器了,代码如下:
def my_print(n):
for i in range(n):
yield(i)
print(my_print(5))
for i in my_print(5):
print(i)
执行结果:
说明:
只是由print改成了yield就得到了一个生成器对象,可以用next()或者for循环将数据取出来。换成yield之后到底发生了什么?
当函数执行到yield i 的时候,函数会把i的数值抛出来,我们用for循环遍历的时候获取了yield 后面的值,然后函数就会暂停,等待下一次遍历的时候,函数从yield继续向下执行,直到遇到yield的时候又返回了i的值,然后函数再暂停,等待下一次唤醒。这个循环一直做,到函数结束的时候。
1.用生成器生成杨辉三角
def triangles(n):
temp_list= [1]
for i in range(1,n+1):
yield temp_list
#每一行是一个list,由上一次计算的结果中每相邻的两个元素之后作为新列表的中间值
temp_list = [temp_list[i]+temp_list[i+1] for i in range(len(temp_list)-1)]
#第一个位置中添加1
temp_list.insert(0,1)
#最后一个位置中添加1
temp_list.append(1)
for i in triangles(10):
print(i)
2.以下代码的执行结果是什么?
def add(n, i):
return n+i
def test():
for i in range(4):
yield i
g = test()
for n in [1,10,5]:
g = (add(n,i) for i in g)
print(list(g))
分析:
g只是代表一个生成器对象,没有for,next,list,tuple调用它的时候它是不会执行的,真实的代码执行逻辑如下:
第1次:n = 1, 执行g=(add(n,i) for i in test())
第2次:n = 10,执行g=(add(n,i) for i in (add(n,i) for i in test())) )
第3次:n = 5,执行g=(add(5,i) for i in (add(n,i) for i in (add(n,i) for i in test())) ))
到第3次的时候n的值就是5,也就是说前两次给n赋的值会覆盖掉,或者可以这样理解,上面的代码等价于:
def add(a,b):
return a + b
def test():
for r in range(4):
yield r
g=test()
n=2
g=(add(n,i) for i in g)
n=10 #覆盖前面n的值
g=(add(n,i) for i in g)
n=5 #覆盖前面n的值
g=(add(n,i) for i in g)
print(list(g))
g因为前面做过定义,每次需要把之前定义的带入,所以当n=5 的时候,g=(add(5,i) for i in (add(n,i) for i in (add(n,i) for i in test())) )),最终的执行结果是[15, 16, 17, 18]
1.碰到for循环套生成器,就拆开来做 ;
2.生成器和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而生成器函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
参考链接:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000
https://www.cnblogs.com/gkx0731/p/9538348.html