生成器

1.什么是生成器:可以理解为一种数据类型,这种数据类型实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__ 方法),所以生成器就是可迭代对象

2.生成器分类在python中的表现形式:(python有两种不同的方式提供生成器)

  (1)生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中,挂起函数的状态,以使下次从他离开的地方继续执行

def test():
    print('开始生孩子了')
    yield ''
    print('开始生儿子了')
    yield '儿子'
    yield '孙子'


res = test()                            res调用了次函数,此时res就相当于一个生成器,支持__next__方法,同时也可以用for循环去遍历它 即 for i in res:print(i) 会将所有内容打印(生孩子---孙子)        
print(res)
print(res.__next__())              
print(res.__next__())
运行结果:
object test at 0x00000198BF0A30F8>        调用函数,相当于返回一个地址  (生成器)   需要通过res.__next__()来显示
开始生孩子了                           
我
开始生儿子了
儿子                                从运行结果可以得知,yield返回了一个值,相当于return,其与return不同之处在于yield会保存当前函数的运行状态,所以第二次运行print(res.__next__())
                                  是从print(‘开始生儿子了’)开始,并在yield ‘儿子’ 处停止并返回值

      卖包子实例

def pro_baozi():
    for i in range(100):
        print('正在生产包子')
        yield '第 %s屉包子生产出来了' %i
        print('\n')
        print('第 %s屉包子卖出去了'%i)

pro_g = pro_baozi()               pro_g调用了函数,此时pro_g就可看作是一个生成器,此时它支持__next__方法
baozi1 =print(pro_g.__next__())
baozi1 =print(pro_g.__next__())
baozi1 =print(pro_g.__next__())
运行结果:
正在生产包子
第 0屉包子生产出来了        函数运行到第一个 yield 暂停并返回值,第二次运行从当前位置开始直到再一次碰到 yield 暂停并返回值
                  此处开始到下一次换行前是第二次调用的结果,因为yield会保留函数的运行状态,所以在第二次调用后,先执行print('\n'),因为print自带换行,因此执行此语句换行了两次

第 0屉包子卖出去了
正在生产包子
第 1屉包子生产出来了


第 1屉包子卖出去了
正在生产包子
第 2屉包子生产出来了

 

  (2)生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

 

    三元表达式

name = '焦国华'
res = '帅哥'   if name == '焦国华'  else  '沙雕'        这种形式就是三元表达式    res = True if 条件  else False
print(res)  
运行结果:

帅哥

 
 

    列表解析

l = ['鸡蛋%s' %i for i in range(10)]      这种形式就称为列表解析,实际上也是[ ]内是一个二元表达式   也可以写为 l = ['鸡蛋%s' %i for i in range(10)  if i >5]   运行输出['鸡蛋 0', '鸡蛋 1', '鸡蛋 2', '鸡蛋 3', '鸡蛋 4']
print(l)
运行结果:['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

    总结:

    (1)生成器表达式   把列表解析的 [ ] 换成()得到的就是生成器表达式

laomuji = ('鸡蛋%s' %i for i in range(10))    此处只是获取了一个生成器,laomuji是一个内存地址,只有再对其进行迭代操作才会输出值,不像三元表达式一样
print(laomuji)
print(laomuji.__next__())
print(laomuji.__next__())
运行结果:
object  at 0x000001AFF8B8F048>
鸡蛋0
鸡蛋1

    (2)列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

    (3)python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是用迭代器协议访问对象。例如,sum函数是python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了

      迭代器协议,所以,我们可以直接这样计算一系列值的和:

sum(x ** 2   for x in range(4))      即内部可以直接写一个生成器表达式,因为生成器表达式满足迭代器协议

 

def get_pop():
    with open('人口普查','r',encoding = 'utf8') as f:
        for i in f:
            yield i

g = get_pop()
all_pop = sum(eval(i)['population']    for i in g)        生成器表达式,前边的是要相加的数。 for i in g 拿到的是字符串形式如‘{'name': '北京','population':'131'}’,通过eval函数
                                      使其变成对应的字典形式,在通过key索引,得到人口数,进而进行相加

 

     

    而不用多此一举先构造一个列表

sum([x ** 2   for x in range(4)])

 

3.生成器总结:

  (1)语法上和函数类似:生成器函数和常规函数几乎是一样的,他们都是使用def语句进行定义,差别在于,生成器使用 yield 语句返回一个值,而常规函数使用 return 语句返回一个值

  (2)自动实现迭代器协议:对于生成器,python会自动实现迭代器协议,以便应用到迭代背景中(如for循环、sum()函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的__next__

    方法,并且,在没有值可以返回的时候,生成器自动生成StopIteration异常

  (3)状态挂起:生成器使用yield语句返回一个值,yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从离开他的地方继续执行

优点::(1)生成器的好处是延时计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这回与大数据量处理将会非常有用

    (2)生成器还能有效提高代码可读性

  (4)生成器只能遍历一次

你可能感兴趣的:(生成器)