目录
🌉一、生成器函数初级进阶
🎇1、从生成器中取值的两种方法
✨(1)、方法一:next方法
def generator(): print(123) yield 1 print(456) yield 2 g1 = generator() #生成器g1 g2 = generator() #生成器g2 print('*',g1.__next__()) #从生成器g1中取第一个值 print('**',g1.__next__()) #从生成器g1中取第二个值 print('***',g2.__next__()) #从生成器g2中取第一个值 输出结果: 123 * 1 456 ** 2 123 *** 1
✨(2)、方法二:send方法
- send 获取下一个值的效果和next基本一致
只是在获取下一个值的时候给上一个yield的位置传递了一个数据
- 使用send的注意事项:
- 第一次使用生成器时候是用next获取下一个值
- 最后一个yield不能接收外部的值
总结:
send:不能用在第一个,取下一个值的时候给上一个位置传一个新的值
def generator(): print(123) content = yield 1 print('=======',content) print(456) yield 2 #send 获取下一个值的效果和next基本一致 #只是在获取下一个值的时候,给上一yield的位置传递一个数据 #使用send的注意事项 # 第一次使用生成器的时候 是用next获取下一个值 # 最后一个yield不能接受外部的值 g = generator() print('*',g.__next__()) ret = g.send('hello') #send的效果与next一样 print('***',ret) 输出结果: 123 * 1 ======= hello 456 *** 2
🎇2、预激协程的装饰器
什么是预激协程的装饰器?
简单来说就是个加了装饰器的生成器(装饰器介绍)
预激协程的装饰器的实例:
#实际上就是省略了avg_g = average() avg_g.__next__()这两步 def init(func): #func = average ''' 装饰器函数 :param func: :return: ''' def inner(*args,**kwargs): g = func(*args,**kwargs) #g = average() g.__next__() return g return inner @init #average = init(average) = inner def average(): sum = 0 count = 0 avg = 0 while 1: #num = yield num = yield avg sum += num count += 1 avg = sum/count avg_g = average() #===>inner ret = avg_g.send(10) print(ret) # avg_g.__next__() # avg1 = avg_g.send(10) # avg2 = avg_g.send(20) # print(avg1,avg2) 输出结果: 10.0
🎇3、Python3新加的yield from
yield from: 后接列表、生成器、协程。与asyncio.coroutine同时使用,定义协程函数。在python3.5以后改成了await。当yield from后面是IO耗时操作的时候,会切换至另一个yield from。
在这我们简单来说:yield可以在函数中代替for循环对返回的数据进行迭代
def generator(): a = 'ab' b = '12' for i in a: yield i for i in b: yield i g = generator() 从生成器中取返回值 for i in g: print(i) 输出结果: a b 1 2 def generator(): a = 'cd' b = '34' yield from a # ,b只能有一个变量 yield from b g = generator() #从生成器中取返回值 for i in g: print(i) 输出结果: c d 3 4
🎇4、回顾
**回顾:\
一、send:\
1、send的作用范围和next一模一样(从一个yield作用到下一个yield)\
2、第一次不能使用send\
3、函数在的最后一个yield不能接收新的值\
二、\
预激生成器的装饰器的例子**\
🌉二、生成器函数高级进阶
🎇1、生成器的表达式和各种推导式
✨(1)、列表推导式
模板:
[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] 遍历后挨个处理\
[满足条件的元素进行相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] 筛选功能egg_list = ['鸡蛋%s'%i for i in range(10)] #这是个列表推导式 print(egg_list) '''相当于''' egg_list = [] for i in range(10): egg_list.append('鸡蛋%s' %i) print(egg_list) print([i for i in range(10)]) 输出结果: ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9'] ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9'] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
✨(2)、生成器的表达式
- 生成器表达式(generator expression)也叫生成器推导式或生成器解析式,用法与列表推导式非常相似,在形式上生成器推导式使用圆括号(parentheses)作为定界符,而不是列表推导式所使用的方括号(square brackets)。
- 与列表推导式最大的不同是,生成器推导式的结果是一个生成器对象。生成器对象类似于迭代器对象,具有惰性求值的特点,只在需要时生成新元素,比列表推导式具有更高的效率,空间占用非常少,尤其适合大数据处理的场合。
- 使用生成器对象的元素时,可以根据需要将其转化为列表或元组,也可以使用生成器对象的next()方法或者内置函数next()进行遍历,或者直接使用for循环来遍历其中的元素。但是不管用哪种方法访问其元素,只能从前往后正向访问每个元素,不能再次访问
- 已访问过的元素,也不支持使用下标访问其中的元素。当所有元素访问结束以后,如果需要重新访问其中的元素,必须重新创建该生成器对象,enumerate、filter、map、zip等其他迭代器对象也具有同样的特点。
g = (i for i in range(10)) print(g) #返回了一个生成器 for i in g: print(i) 输出结果: <generator object <genexpr> at 0x000002075E40FC10> 0 1 2 3 4 5 6 7 8 9
✨(3)、字典推导式
例一:将一个字典的key和value对调
dic = {'a' : 97,'b' : 98} dic_exchange = {dic[k] :k for k in dic} print(dic_exchange) 输出结果: {97: 'a', 98: 'b'}
✨(4)、集合推导式(自带结果去重功能)
set = {x**2 for x in [1,-1,2,-2,3,4]} print(set) 输出结果: {16, 1, 4, 9}
🎇2、总结
**唯独没有元组推导式,要得到一个元组就可以直接将推出来的数据类型转换为元组就行\
总结:各种推导式:生成器 列表 字典 集合\
1、遍历操作\
2、筛选操作\
二、惰性运算\
生成器与迭代器都是惰性运算:\
但是生成器你可以看的见因为这是你写的而迭代器一般看不见 \
1、同一生成器中的数据只能取一次取完就没了\
2、惰性运算:不找它要值它就不返回\列表解析\
sum([i for i in range(100000000)]) # 内存占用大,机器容易卡死**
**# 生成器表达式\
sum(i for i in range(100000000)) # 几乎不占内存\
1.把列表解析的[]换成()得到的就是生成器表达式**2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。