生成器的定义: 生成器它的本质就是迭代器
我们知道的迭代器有两种: 一种是调用方法直接返回的, 一种是可迭代对象通过执行 iter 方法得到的, 迭代器有的好处是可以节省内存.
如果在某些情况下, 我们也需要节省内存, 就只能自己写. 我们自己写的这个能实现迭代器功能的东西就叫生成器
在 python 中有以下几种方式来获取生成器
1. 通过生成器函数
2. 通过各种推到式来实现生成器
首先, 我们先看一个很简单的函数:
- def func():
- print(11)
- return 22
- ret = func()
- print(ret)
- # 运行结果:
- 11
- 22
我们只需要修改一个地方就可以把函数变成生成器 就是将函数中的 return 换成 yield 就是生成器
Python 中提供的生成器:
1. 生成器函数: 常规函数定义, 但是, 使用 yield 语句而不是 return 语句返回结果. yield 语句一次返回一个结果, 在每个结果中间, 挂起函数的状态, 以便下次重它离开的地方继续执行
2. 生成器表达式: 类似于列表推导, 但是, 生成器返回按需产生结果的一个对象, 而不是一次构建一个结果列表
生成器 Generator:
本质: 迭代器(所以自带了__iter__方法和__next__方法, 不需要我们去实现)
特点: 惰性运算, 开发者自定义
定义生成器
- def func():
- print(11)
- yield 22
- ret = func()
- print(ret)
- # 运行结果:
- <generator object func at 0x000001A575163888>
一个包含 yield 关键字的函数就是一个生成器函数. yield 可以为我们从函数中返回值, 但是 yield 又不同于 return,return 的执行意味着程序的结束, 调用生成器函数不会得到返回的具体的值, 而是得到一个可迭代的对象. 每一次获取这个可迭代对象的值, 就能推动函数的执行, 获取新的返回值. 直到函数执行结束.
- import time
- def genrator_fun1():
- a = 1
- print('现在定义了 a 变量')
- yield a
- b = 2
- print('现在又定义了 b 变量')
- yield b
- g1 = genrator_fun1()
- print('g1 :',g1) #打印 g1 可以发现 g1 就是一个生成器
- print('-'*20) #我是华丽的分割线
- print(next(g1))
- time.sleep(1) #sleep 一秒看清执行过程
- print(next(g1))
- print("111")
- yield 222
- print("333")
- yield 444
- gener = func()
- ret = gener.__next__()
- print(ret)
- ret2 = gener.__next__()
- print(ret2)
- ret3 = gener.__next__()
- # 最后? 个 yield 执? 完毕. 再次__next__()程序报错
- print(ret3)
结果:
- 111
- 222
- 333
- 444
当程序运行完最后一个 yield, 那么后面继续运行__next__()程序会报错
好了生成器我们认识了, 生成器有什么作用呢?
生成器有什么好处呢? 就是不会一下子在内存中生成太多数据
假如我想让工厂给学生做校服, 生产 2000000 件衣服, 我和工厂一说, 工厂应该是先答应下来, 然后再去生产, 我可以一件一件的要, 也可以根据学生一批一批的找工厂拿.
而不能是一说要生产 2000000 件衣服, 工厂就先去做生产 2000000 件衣服, 等回来做好了, 学生都毕业了...
- def produce():
- """生产衣服"""
- for i in range(2000000):
- yield "生产了第 %s 件衣服"%i
- product_g = produce()
- print(product_g.__next__()) #要一件衣服
- print(product_g.__next__()) #再要一件衣服
- print(product_g.__next__()) #再要一件衣服
- num = 0
- for i in product_g: #要一批衣服, 比如 5 件
- print(i)
- num +=1
- if num == 5:
- break
- send:
- def generator():
- print(123)
- content = yield 1
- print('=======',content)
- print(456)
- yield2
- g = generator()
- ret = g.__next__()
- print('***',ret)
- ret = g.send('hello') #send 的效果和 next 一样
- print('***',ret)
- #send 获取下一个值的效果和 next 基本一致
- # 只是在获取下一个值的时候, 给上一 yield 的位置传递一个数据
- # 使用 send 的注意事项
- # 第一次使用生成器的时候 是用 next 获取下一个值
- # 最后一个 yield 不能接受外部的值
- yield from
yield from 在 python3 中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
- def func():
- lst = ['卫龙','老冰棍','北冰洋','牛羊配']
- yield from lst
- g = func()
- for i in g:
- print(i)
结果:
卫龙
老冰棍
北冰洋
牛羊配
有个小坑, yield from 是将列表中的每一个元素返回, 所以 如果写两个 yield from 并不会产生交替的效果
- def func():
- lst1 = ['卫龙','老冰棍','北冰洋','牛羊配']
- lst2 = ['馒头','花卷','豆包','大饼']
- yield from lst1
- yield from lst2
- g = func()
- for i in g:
- print(i)
结果:
卫龙
老冰棍
北冰洋
牛羊配
馒头
花卷
豆包
大饼
推导式:
列表推导式:
列表推导式, 生成器表达式以及其他推导式, 首先我们先看一下这样的代码, 给出一个列表, 通过循环, 想列表中添加 1~10
- li = []
- for i in range(10):
- li.append(i)
- print(li)
我们换成列表推导式是什么样的, 来看看:
列表推导式的常? 写法:
[结果 for 变量 in 可迭代对象]
- ls = [i for i in range(10)]
- print(ls)
列表推导式是通过? 行来构建你要的列表, 列表推导式看起来代码简单. 但是出现错误之后很难排查.
- lst = ['python%s' % i for i in range(1,19)]
- print(lst)
筛选模式:
[结果 for 变量 in 可迭代对象 if 条件]
- lst = [i for i in range(100) if i %2 == 0]
- print(lst)
生成器推导式:
生成器表达式和列表推导式的语法基本上一样的, 只是把 [] 换成()
- gen = (i for i in range(10))
- print(gen)
- # 结果: <generator object <genexpr> at 0x0000026046CAEBF8>
打印的结果就是一个生成器, 我们可以使用 for 循环来循环这个生成器
- gen = ("第 %s 次" % i for i in range(10))
- for i in gen:
- print(i)
生成器表达式也可以进行筛选
- # 获取 1-100 内能被 3 整除的数
- gen = (i for i in range(1,100) if i % 3 == 0)
- for num in gen:
- print(num)
- # 100 以内能被 3 整除的数的平?
- gen = (i * i for i in range(100) if i % 3 == 0)
- for num in gen:
- print(num)
- # 寻找名字中带有两个 e 的人的名字
- names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
- ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
- # 不用推导式和表达式
- result = []
- for first in names:
- for name in first:
- if name.count("e")>= 2:
- result.append(name)
- print(result)
- # 推导式
- gen = (name for first in names for name in first if name.count('e')>= 2)
- for i in gen:
- print(i)
生成器表达式和列表推导式的区别:
1. 列表推导式比较耗内存, 一次性加载. 生成器表达式几乎不占用内存. 使用的时候才分配和使用内存
2. 得到的值不一样, 列表推导式得到的是一个列表. 生成器表达式获取的是一个生成器
3..Python 不但使用迭代器协议, 让 for 循环变得更加通用. 大部分内置函数, 也是使用迭代器协议访问对象的. 例如, sum 函数是 Python 的内置函数, 该函数使用迭代器协议访问对象, 而生成器实现了迭代器协议, 所以, 我们可以直接这样计算一系列值的和:
- sum(x ** 2 for x in range(4))
- def func():
- print(111)
- yield 222
- g = func() # 生成器 g
- g1 = (i for i in g) # 生成器 g1. 但是 g1 的数据来源于 g
- g2 = (i for i in g1) # 生成器 g2. 来源 g1
- # list 的底层有 for 循环, for 就是一直执行__next__() 所以可以将生成器放到 list 中
- print(list(g)) # 获取 g 中的数据. 这时 func()才会被执行. 打印 111. 获取到 222. g 完毕.
- print(list(g1)) # 获取 g1 中的数据. g1 的数据来源是 g. 但是 g 已经取完了. g1 也就没有数据了
- print(list(g2)) # 和 g1 同理理
- print(next(g))
- print(next(g1))
- print(next(g2)) # 可以用 next 来验证 其实 list 就是将内容迭代了转换成了列表
字典推导式:
- lst1 = ['jay','jj','meet']
- lst2 = ['周杰伦','林俊杰','郭宝元']
- dic = {
- lst1[i]:lst2[i] for i in range(len(lst1))
- }
- print(dic)
集合推导式:
集合推导式可以帮我们直接生成一个集合, 集合的特点; 无序, 不重复 所以集合推导式自带去重功能
- lst = [1,2,3,-1,-3,-7,9]
- s = {
- abs(i) for i in lst
- }
- print(s)
总结:
? 推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
? 生成器表达式: (结果 for 变量量 in 可迭代对象 if 条件筛选)
? 生成器表达式可以直接获取到? 成器对象. ? 成器对象可以直接进行 for 循环. ? 成器具有惰性机制.
? 集合推导式和字典推导式很是类似, 记住一个小技巧能够快速区分那个是字典那个是集合
? 字典推导式前面的结果是有个冒号, 而集合的前面结果就是单纯的结果
作业:
2. 用列表推导式做下列小题
Li = [1,23,4,5,6,'a','b','c','d']
a. 过滤掉长度小于 3 的字符串列表, 并将剩下的转换成大写字母
b. 求 (x,y) 其中 x 是 0-5 之间的偶数, y 是 0-5 之间的奇数组成的元祖列表
c. M = [[1,2,3],[4,5,6],[7,8,9]] 求 M 中 3,6,9 组成的列表
d. 求出 50 以内能被 3 整除的数的平方, 并放入到一个列表中.
e. 构建一个列表:['python1 期', 'python2 期', 'python3 期', 'python4 期', 'python6 期', 'python7 期', 'python8 期', 'python9 期', 'python10 期']
f. 构建一个列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
g. 构建一个列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
h. 有一个列表 l1 = ['alex', 'WuSir', '老男孩', '太白']将其构造成这种列表['alex0', 'WuSir1', '老男孩 2', '太白 3']
(9)有以下数据类型:
- x = {
- 'name':'alex',
- 'Values':[{'timestamp':1517991992.94,
- 'values':100,},
- {'timestamp': 1517992000.94,
- 'values': 200,},
- {'timestamp': 1517992014.94,
- 'values': 300,},
- {'timestamp': 1517992744.94,
- 'values': 350},
- {'timestamp': 1517992800.94,
- 'values': 280}
- ],}
将上面的数据通过列表推导式转换成下面的类型:[[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]
Python 入门基础知识点(生成器和推导式)
来源: http://www.bubuko.com/infodetail-2990234.html