1、生成器的定义
只要含有yield关键字的函数都是生成器函数
yield不能跟return共用,且需要写在函数内部
生成器函数执行之后会得到一个生成器作为返回值
1 def generator(): 2 print(1) 3 yield 'a' #有了yield就不要有return 4 print(2) 5 yield 'b' 6 print(3) 7 yield 'c' 8 g = generator() 9 ret = g.__next__() 10 print(ret) 11 ret = g.__next__() 12 print(ret) 13 ret = g.__next__() 14 print(ret)
1 1 2 a 3 2 4 b 5 3 6 c
1 def generator(): 2 print(1) 3 yield 'a' #有了yield就不要有return 4 print(2) 5 yield 'b' 6 print(3) 7 yield 'c' 8 g = generator() 9 for i in g: 10 print(i)
1 1 2 a 3 2 4 b 5 3 6 c
1 l = [1,2,3,4,5] #列表是可迭代的,但并不是迭代器 2 for i in l: 3 print(i) 4 if i == 2: 5 break 6 for i in l: 7 print(i)
1 1 2 2 3 1 4 2 5 3 6 4 7 5
1 def func(): 2 for i in range(233333): 3 yield '好日子%s'%i 4 g = func() 5 g2 = func() 6 print(g.__next__()) 7 print(g.__next__()) 8 print(g.__next__()) 9 print(g.__next__()) 10 print(g2.__next__()) 11 print(g2.__next__())
1 好日子0 2 好日子1 3 好日子2 4 好日子3 5 好日子0 6 好日子1 7 8 结论:g和g2是两个独立的生成器
下面是生成器函数的基本执行顺序
2、监听文件输入的例子
假定在相当路径下已经file这个文件
1 f = open("file",encoding="utf-8") 2 while True: 3 line = f.readline() 4 if line: 5 print(line.strip())
1 fdfdfd 2 fdfdfdfd 3 fdfdfdfd 4 fdfdfdfdfdfdf 5 dfegrffff 6 hello 7 你好hello
就上面的代码,怎么在使用生成器的情况下能够监听文件中指定的内容呢?
1 比如我要用下面的代码进行监听有hello的情况下,进行打印,且打印是以$$$开头? 2 3 for i in g: 4 if "hello" in i: 5 print('$$$',i)
1 def tail(filename): 2 f = open(filename,encoding="utf-8") 3 while True: 4 line = f.readline().strip() #如果这里不加strip(),那么下面会把回车也当一行来执行 5 if line: 6 yield line.strip() 7 g = tail('file') 8 for i in g: 9 if "hello" in i: 10 print('$$$',i)
1 $$$ hello 2 $$$ 你好hello
3、生成器与迭代器
迭代器:
双下标法:很少直接调用的方法,一般情况下,是通过其它语法触发的。
可迭代的:可迭代协议,含有__iter__的方法('__iter__' in dir(数据))
可迭代的一定可以被for循环
迭代器协议:含有__iter__和__next__方法
迭代器一定可以迭代,可迭代的通过调用iter()方法就能得到一个迭代器
迭代器的特点:
很方便使用,且只能取所有的数据取一次
节省内存空间
生成器:
生成器的本质就是迭代器
生成器的表现形式:
生成器函数
生成器表达式
生成器函数:
含有yield关键字的函数就是生成器函数
特点:
调用函数之后函数不执行,返回一个生成器
每次调用next方法的时候会取到一个值
从生成器取值的几个方法:
1、next
2、for
3、数据类型的强制转换:占用内存
数据类型强制转换举例:
1 def generator(): 2 for i in range(100): 3 yield '周六加班%s'%i 4 5 g = generator() #调用生成器函数,产生一个生成器 6 print(list(g)) #强制转换生成器为列表,这是获得生成器数据的一种方式 7 8 结果: 9 ['周六加班0', '周六加班1', '周六加班2', '周六加班3', '周六加班4', '周六加班5', '周六加班6', '周六加班7', '周六加班8', '周六加班9', '周六加班10', '周六加班11', '周六加班12', '周六加班13', '周六加班14', '周六加班15', '周六加班16', '周六加班17', '周六加班18', '周六加班19', '周六加班20', '周六加班21', '周六加班22', '周六加班23', '周六加班24', '周六加班25', '周六加班26', '周六加班27', '周六加班28', '周六加班29', '周六加班30', '周六加班31', '周六加班32', '周六加班33', '周六加班34', '周六加班35', '周六加班36', '周六加班37', '周六加班38', '周六加班39', '周六加班40', '周六加班41', '周六加班42', '周六加班43', '周六加班44', '周六加班45', '周六加班46', '周六加班47', '周六加班48', '周六加班49', '周六加班50', '周六加班51', '周六加班52', '周六加班53', '周六加班54', '周六加班55', '周六加班56', '周六加班57', '周六加班58', '周六加班59', '周六加班60', '周六加班61', '周六加班62', '周六加班63', '周六加班64', '周六加班65', '周六加班66', '周六加班67', '周六加班68', '周六加班69', '周六加班70', '周六加班71', '周六加班72', '周六加班73', '周六加班74', '周六加班75', '周六加班76', '周六加班77', '周六加班78', '周六加班79', '周六加班80', '周六加班81', '周六加班82', '周六加班83', '周六加班84', '周六加班85', '周六加班86', '周六加班87', '周六加班88', '周六加班89', '周六加班90', '周六加班91', '周六加班92', '周六加班93', '周六加班94', '周六加班95', '周六加班96', '周六加班97', '周六加班98', '周六加班99']
next方式举例
def generator(): for i in range(100): yield '周六加班%s'%i g = generator() #调用生成器函数,产生一个生成器 ret = g.__next__() #每次执行一次__next__就从生成器中取值,即预示着函数继续执行 print(ret) ret = g.__next__() print(ret) ret = g.__next__() print(ret) 结果: 周六加班0 周六加班1 周六加班2
for循环举例:
1 def generator(): 2 for i in range(100): 3 yield '周六加班%s'%i 4 5 g = generator() #调用生成器函数,产生一个生成器 6 num = 0 7 for i in g: 8 num += 1 9 if num > 45 : 10 break 11 print(i) 12 结果: 13 周六加班0 14 周六加班1 15 周六加班2 16 周六加班3 17 周六加班4 18 周六加班5 19 周六加班6 20 周六加班7 21 周六加班8 22 周六加班9 23 周六加班10 24 周六加班11 25 周六加班12 26 周六加班13 27 周六加班14 28 周六加班15 29 周六加班16 30 周六加班17 31 周六加班18 32 周六加班19 33 周六加班20 34 周六加班21 35 周六加班22 36 周六加班23 37 周六加班24 38 周六加班25 39 周六加班26 40 周六加班27 41 周六加班28 42 周六加班29 43 周六加班30 44 周六加班31 45 周六加班32 46 周六加班33 47 周六加班34 48 周六加班35 49 周六加班36 50 周六加班37 51 周六加班38 52 周六加班39 53 周六加班40 54 周六加班41 55 周六加班42 56 周六加班43 57 周六加班44
4、生成器中send的作用
send:send的效果其实跟next相同
只是在获取下一个值的时候,给上一个yield的位置传递一个数据
使用Send的注意事项:
第一次使用生成器的时候,是用next获取下一个值,即第一次得先使用一次next
最后一个yield不能接收外部的数据,那么最后一个yield就直接返回为空即可
1 def generator(): 2 print(123) 3 content = yield 2 4 print('&&&&&&',content) 5 print(333) 6 arg = yield 4 7 ''' 8 这里可以执行一些要执行的事情 9 ''' 10 yield #最后一个yield为空,即不返回任何值 11 12 g = generator() 13 ret = g.__next__() 14 print('***',ret) 15 ret = g.send('Hello Word') #send的效果和next相同 16 #Hello Word实际是yield 2执行完以后,函数等待往下执行时候,把Hello Word发给函数 17 #实际就是传给了content 18 print('$$$',ret)
1 123 2 *** 2 3 &&&&&& Hello Word 4 333 5 $$$ 4
4.1 使用send的例子:
获取移动平均值需求随着时间的推移,那么数值会变化,那么平均值也会变化
下面这段代码报错的原因是什么,怎么改进?
1 def average(): 2 sum = 0 3 count = 0 4 avg = 0 5 while True: 6 sum += num 7 count += 1 8 avg = sum / count 9 num = yield avg 10 g_avg = average() 11 ret = g_avg.__next__() 12 print(ret) 13 ret = g_avg.send(10) 14 print(ret) 15 ret = g_avg.send(30) 16 print(ret)
改进版本:
1 def average(): 2 sum = 0 3 count = 0 4 avg = 0 5 while True: 6 num = yield avg 7 sum += num 8 count += 1 9 avg = sum / count 10 11 g_avg = average() 12 ret = g_avg.__next__() 13 print(ret) 14 ret = g_avg.send(10) 15 print(ret) 16 ret = g_avg.send(30) 17 print(ret)
1 0 2 10.0 3 20.0
使用装饰器以后的改进版本:用一个装饰器,使得send时候不用得先执行一次next,用一个装饰器把这个生成器激活,
即在装饰器里面执行一次next
1 def init(func): 2 def inner(*args, **kwargs): 3 g = func(*args, **kwargs) 4 g.__next__() 5 return g 6 7 return inner 8 9 10 @init 11 def average(): 12 sum = 0 13 count = 0 14 avg = 0 15 while True: 16 num = yield avg 17 sum += num 18 count += 1 19 avg = sum / count 20 21 22 g_avg = average() 23 ret = g_avg.send(10) 24 print(ret) 25 ret = g_avg.send(30) 26 print(ret)
1 10.0 2 20.0
4.2 Python 3里面的yield from的用法:
有下面一段代码,请用yield from的方法来优化:
1 def generator(): 2 a = 'abcd' 3 b = '12345' 4 for i in a: 5 print(i) 6 for i in b: 7 print(i) 8 generator() 9 结果: 10 a 11 b 12 c 13 d 14 1 15 2 16 3 17 4 18 5
优化后的代码:
1 def generator(): 2 a = 'abcd' 3 b = '12345' 4 yield from a 5 print(a) 6 yield from b 7 print(b) 8 ret = generator() 9 ret.__next__() 10 print(ret) 11 for i in ret: 12 print(i) 13 14 结果: 15 <generator object generator at 0x000000000296A728> 16 b 17 c 18 d 19 abcd 20 1 21 2 22 3 23 4 24 5 25 12345
5、各种推导式
各种推导式 : 生成器 列表 字典 集合
遍历操作
筛选操作
列表生成式与生成器表达式的区别:
1、括号不一样
2、返回值不一样,生成器返回值几乎不占内存
g = (i * i for i in range(10)) #这里只是产生一个生成器
g.__next__() #只有运行以后,生成器g里面才会有具体的值
列表生成式:
1 egg = ["想睡觉%s"%i for i in range(10)] #列表生成式 2 print(egg)
1 结果: 2 ['想睡觉0', '想睡觉1', '想睡觉2', '想睡觉3', '想睡觉4', '想睡觉5', '想睡觉6', '想睡觉7', '想睡觉8', '想睡觉9']
生成器表达式:
1 g = (i for i in range(10)) #生成器表达式 2 for i in g: 3 print(i)
结果:
1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9
5.1列表推导式
[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理
[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] 筛选功能
例1 : 求30以内能被3整除的数
1 ret = [i for i in range(30) if i%3 == 0] #完整的列表推导式 2 g = (i for i in range(30) if i%3 == 0) #生成器推导式 3 print(ret) 4 5 结果: 6 [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例2 :求30以内能被3整除的平方
1 ret = [i*i for i in range(30) if i%3 == 0] #列表推导式 2 g2 = (i*i for i in range(30) if i%3 == 0) #生成器推导式 3 print(ret) 4 5 结果: 6 [0, 9, 36, 81, 144, 225, 324, 441, 576, 729]
例3 : 找出嵌套列表中含有两个'e'的所有名字
1 names = [['penge','tom33e','Steven'],['Jennifer','Sherry']] 2 ret = [ name for lis in names for name in lis if name.count('e') == 2] #列表推导式 3 g3 = (name for lis in names for name in lis if name.count('e') == 2) #生成器推导式 4 print(ret) 5 6 结果: 7 ['penge', 'Steven', 'Jennifer']
5.2 字典推导式
例一:将一个字典的key和value对调
1 mcase = {'a': 10, 'b': 34} 2 mcase_frequency = {mcase[k]: k for k in mcase} 3 print(mcase_frequency) 4 5 结果: 6 {10: 'a', 34: 'b'}
例2 : 合并大小写对应的value值,将k统一成小写
1 mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} 2 mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()} 3 print(mcase_frequency) 4 5 结果: 6 {'a': 17, 'b': 34, 'z': 3}
5.3 集合推导式
例1:计算列表中每个值的平方,自带去重功能
1 squared = {x**2 for x in [1, -1, 2]} 2 print(squared) 3 4 结果: 5 {1, 4}