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__()) 运行结果: <generator 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(' ') 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(' '),因为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__()) 运行结果: <generator object <genexpr> 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)生成器只能遍历一次