python3基础:生成器

前言

在python中,边循环边计算的机制就称为生成器(generator)。使用生成器当用到数据的时候在生成,这样可以节约空间,提高效率。生成器是一种特殊类型的迭代器,所以也会有__init__()和__next__()两个方法:

__iter__方法负责返回一个迭代器(在迭代器中返回自己,在可迭代对象中返回帮助自己迭代的迭代器)

_next__方法当前要获取的元素没有界,就返回当前元素,然后自己指向为下一个元素等待返回;如果上次反回了最后一个元素,这一次再调用next的时候已经没有元素了,就抛出StopIteration异常。

两种实现方式

1.( ) 括号内 放入列表推倒表达式 返回一个生成器对象

  • 用next()方法取出元素:
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))#报错,因为没有元素可以取了

执行结果:

 python3基础:生成器_第1张图片

说明:

 写法和推导列表非常相似,只是由[]换成了()。由于生成器也是迭代器,所以可以使用next()方法将元素一个一个取出来,当所有的元素都取出后再调用next()方法就会抛出StopIteration异常。

  • 利用for循环取出元素:
myIterator = ( x*2 for x in range(11))
print(type(myIterator))
for i in myIterator:
    print(i,end=" ")

执行结果: 

 

说明:

由于生成器也是迭代器所以可以放在for循环中操作,将元素一个一个取出来。可以发现使用这种方法不会有异常抛出,因为for循环内部已经处理了。

2.yield关键字

比如我们定义一个函数,打印生成n以内的整数,代码如下:

def my_print(n):
      for i in range(n):
            print(i)

my_print(5)

 执行结果:

python3基础:生成器_第2张图片

当把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)

执行结果:

 python3基础:生成器_第3张图片

说明:

只是由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)

 python3基础:生成器_第4张图片

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

 

你可能感兴趣的:(python)