一、迭代器协议
1、什么是迭代器协议
(1)、迭代器协议指对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,
要么就引起一个StopIteration异常,以终止迭代(只能往后奏不能往前走)
(2)、可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义了一个__iter__()方法)
(3)、协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(for循环、sum、min、max函数)
使用迭代器协议访问对象
二、for循环机制
for循环的本质:循环所有对象,全都是使用迭代器协议
备注:for循环的本质就是遵循迭代器协议去访问对象,那么for循环的对象可定都是迭代器了啊,么错,
既然这样,for循环可以遍历(字符串、列表、元组、字典、集合、文件对象),那这些类型的数据肯定都是可迭代对象啊?但是,为什么我定义了一个列表l=[1,2,3,4,4]没有next()方法?
——字符串、列表、元组、字典、集合、文件对象这些都不是可迭代对象,只不过在for循环中,调用了他们
内部的__iter__方法,把他们变成了可迭代对象
———for循环调用可迭代对象的__next__方法进行取值,而且for循环会捕捉Stopiteration异常,以终止迭代
1、for循环工作机制的模拟
1 l = [1,2,3] #将列表赋值为l 2 # for i in l: #使用for循环进行遍历list中的所有元素 3 # print(i) 4 5 #第一种 采用脚标的方式访问列表中的元素 6 7 #脚标方式是能访问有序的数据类型,无序的数据类型无法通过index进行访问 8 # l_index = 0 9 # while l_index<len(l): 10 # print(l[l_index]) 11 # l_index+=1 12 13 # 第二种:模拟for循环的工作机制 14 15 #第一步:调用对象内部的__iter__()方法,遵循迭代器协议 16 iter_l = l.__iter__() 17 #第二步:调用可迭代对象的next()方法,然后捕捉异常,防止程序崩溃 18 # print(iter_l) #<list_iterator object at 0x000001D1B459F940> 遵循迭代器协议,生成可迭代对象 19 # print(iter_l.__next__()) #第一次调用next方法
#等价于print(next(iter_l))
next()方法是python的内置函数,本质上就是在调用对象的__next__()方法 20 # print(iter_l.__next__()) #第二次调用next方法 21 # print(iter_l.__next__()) #第三次调用next方法 22 # print(iter_l.__next__()) #第四次调用next方法 #StopIteration 23 while True: 24 try: 25 print(iter_l.__next__()) 26 except StopIteration: 27 # print("对象迭代完毕,循环终止") 28 break 29
三、生成器
1、什么是生成器
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的
__iter__()方法),直接就有__next__()方法,所以生成器就是可迭代对象
2、生成器分类
在python中有两种不同的方式提供生成器
(1)、生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
1 #生成器函数 2 def test(): 3 yield 1 4 yield 2 5 yield 3 6 g = test() 7 print(g) #<generator object test at 0x000001A1951D0990> 8 print(g.__next__()) #1 9 print(g.__next__()) #2 10 print(g.__next__()) #3 11 print(g.__next__()) # StopIteration
备注:
a、生成器函数语法上和函数类似:生成器函数和常规函数几乎是一样的。他们都是使用def
语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值
b、自动实现迭代器协议:对于生成器,python会自动实现迭代器协议
c、状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的
信息,以便以后从它离开的地方继续执行
(2)、生成器表达式:类似于列表推导,但是生成器返回按需生成结果的一个对象,而不是一次构建一个结果列表
1 #列表解析 2 l = ['鸡蛋%s' %i for i in range(10) ] 3 print(l,type(l)) #<class 'list'> 列表解析产生的是一个列表 4 5 #生成器函数 6 def test(): 7 for i in range(10): 8 yield '鸡蛋%s' %i 9 g = test() 10 print(g) #<generator object test at 0x000001A1951D0990> 11 print(g.__next__()) #鸡蛋0 12 print(g.__next__()) #鸡蛋1 13 print(g.__next__()) #鸡蛋2 14 print(g.__next__()) # 鸡蛋3 15 16 17 #生成器表达式 18 laomuji = ('鸡蛋%s' %i for i in range(10)) #生成器表达式 19 #生成器表达式就是将列表解析的中括号变为小括号就变成了生成器表达式 20 #列表解析在内存中把整个列表都生成出来;生成器表达式是按需取值,节省内存 21 print(laomuji) #<generator object <genexpr> at 0x00000180833A0990>生成器 22 print(laomuji.__next__()) #鸡蛋0 23 print(laomuji.__next__()) #鸡蛋1 24 print(next(laomuji)) #鸡蛋2
备注:
a、把列表解析的[]换成()得到的就是生成器表达式
b、列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
c、python使用迭代器协议让for循环变得更加通用,大部分内置函数也是使用迭代器协议访问对象的
。如sum()函数是python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议
,所以我们可以直接这样计算一系列值的和:
1 print(sum(x**2 for x in range(100))) #328350
3、生成器优点
python使用生成器对延迟操作提供了支持。延迟操作就是指在需要的时候才产生结果,而不是立即产生结果
4、生成器小结
(1)、生成器是可迭代对象
(2)、实现了延迟计划,节省内存
(3)、生成器本质和其他数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算节省
内存的好处
四、三元表达式及列表解析
1、三元表达式
三元表达式格式:
为真时的结果 if 判断条件 else 为假时的结果(注意,没有冒号)
1 input_l = input("请输入你的名字,少年:") 2 #第一种:if else条件语句进行判断 3 # if input_l =='alex': 4 # print('不合法字符,请重新输入:') 5 # else: 6 # print('注册成功,骚年') 7 #第二种:使用三元表达书进行书写 8 input_l_res = '不合法字符,请重新输入:' if input_l == 'alex' else '注册成功,骚年' 9 #第一元:if input_l == 'alex' 10 #第二元:'不合法字符,请重新输入:' 当第一元结果为True,返回第二元 11 #第三元:'注册成功,骚年' 当第一元结果为False,返回第三元 12 print(input_l_res)
2、列表解析
列表解析使用三元表达式与字符串拼接实现
1 #列表解析 生成一筐鸡蛋 2 # 第一种方法:使用for循环 3 # egg_list = [] 4 # for i in range(10): # 5 # egg_list.append('鸡蛋%s' %i) #字符串拼接 6 # print(egg_list) 7 8 # 第二种方法:使用列表解析 9 l = ['鸡蛋%s' %i for i in range(10) ] #二元表达式 10 l1 = ['鸡蛋%s' %i for i in range(10) if i >5 ] #三元表达式 11 print(l)
备注:三元表达式当处理数据量比较大的时候占用内存比较大
五、生成器模拟生产者消费者模型
import time def consumer(name): print("我叫%s,我要一份凉粉" %name) while True: liangfen =yield time.sleep(1) print('%s狼吞虎咽地把%s吃完了' %(name,liangfen)) def producer(): c1 = consumer('张三') c1.__next__() for i in range(10): time.sleep(1) c1.send('第%s碗凉粉' %i) producer()
1、触发生成器运行的方法
a、_next_方法
b、next方法
c、send方法
2、备注:
yield相当于return,控制的是函数的返回值;
x=yeild的另外一个特性,接收send传过来的值,赋值给x;