递归和迭代
迭代:迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
迭代器协议
1、迭代器协议:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个stopiteration异常,以终止迭代(只能往后走不能往前退)
2、可迭代对象:实现了迭代器协议的对象(__iter__())
注意:字符串、列表、元组、字典、集合、文件对象都不是可迭代对象,只不过在for循环中,调用了__iter__()方法,把它们变成了可迭代对象。
1 l = [1,2,3] 2 iter_l = l.__iter__()#遵循迭代器协议转化为可迭代对象 3 print(iter_l.__next__()) 4 print(iter_l.__next__()) 5 print(iter_l.__next__()) 6 # 1 7 # 2 8 # 3
异常终止
1 l = [1,2,3] 2 iter_l = l.__iter__() # 遵循迭代器协议转化为可迭代对象 3 while True: 4 try: 5 print(iter_l.__next__()) 6 except StopIteration : 7 print("运行终止") 8 break 9 # 1 10 # 2 11 # 3 12 # 运行终止
注意:next()就是调用 iter__.next__()
3、协议是一种约定,可迭代对象实现了迭代器协议,Python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
生成器
生成器:可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象
生成器在Python中的表现形式:两种
1、生成器函数:常规函数定义,但使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
1 def test(): 2 yield 1 3 g = test() 4 print(g) 5 print(g.__next__()) 6 # <generator object test at 0x000001EC499EF9A8> 7 # 1 8 print(g.__next__()) 9 #StopIteration
生成器函数的好处:
1、效率高:得到一个结果可以立刻进行下一步处理,无需等待其他运算结果
2、减少内存占用
生产冰淇淋出售:
1 def produce_ice(): 2 for i in range(100): 3 yield "冰淇淋%s" %i 4 ice_cream = produce_ice() 5 customer1 = ice_cream.__next__() 6 print("来了一个客户买了%s" %customer1) 7 customer2 = ice_cream.__next__() 8 print("来了一个客户买了%s" %customer2) 9 # 来了一个客户买了冰淇淋0 10 # 来了一个客户买了冰淇淋1
分发礼物:
1 def distribute_gift(): 2 for i in range(1000): 3 print("开始发放礼物") 4 yield "礼物%s"%i 5 print("继续生产礼物") 6 7 gift = distribute_gift() 8 employee0 = gift.__next__() 9 print("员工0获得了%s"%employee0) 10 employee1 = gift.__next__() 11 print("员工1获得了%s"%employee1) 12 employee2 = gift.__next__() 13 print("员工2获得了%s"%employee2) 14 # 开始发放礼物 15 # 员工0获得了礼物0 16 # 继续生产礼物 17 # 开始发放礼物 18 # 员工1获得了礼物1 19 # 继续生产礼物 20 # 开始发放礼物 21 # 员工2获得了礼物2
2、生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
1 dog = ("小狗狗%s"%i for i in range(10) if i > 0) 2 print(dog.__next__()) 3 # 小狗狗1 4 print(next(dog)) 5 # 小狗狗2
补充:三元表达式
注意:没有四元表达式
1 name = "ally" 2 res = "Right" if name =="ally" else "Wrong" #如果True返回“right”,False返回“Wrong” 3 print(res) 4 #Right 5 name = "wwqwey" 6 res = "Right" if name =="ally" else "Wrong" #如果True返回“right”,False返回“Wrong” 7 print(res) 8 #Wrong
列表解析
原始方式
1 egg = [] 2 for i in range(10): 3 egg.append("鸡蛋%s" %i) 4 print(egg) 5 # ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
使用列表解析方法
1 egg= ["鸡蛋%s"%i for i in range(10)] 2 print(egg) 3 # ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
写成三元表达式
1 egg= ["鸡蛋%s"%i for i in range(10) if i > 5] 2 print(egg) 3 # ['鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']
注意:
1、把列表解析的[]换成()就是生成器表达式
2、列表解析和生成器表达式都是一种便利的编程方式,都是生成器表达式更省内存
3、Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数也使用迭代器协议访问对象。比如:sum()函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议。所以,我们可以直接计算这样一系列值的和:
1 print(sum(x **2 for x in range(4))) 2 print(sum(x for x in range(10000000))) 3 # 14 4 # 49999995000000
生成器总结:
1、语法和函数类似:生成器函数和常规函数几乎一致,都使用def语句定义,差别在于生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值。
2、自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for 循环,sum函数)。由于生成器自动实现了迭代器协议,所以可以调用其next方法,比你高且,在无值可返回的情况下,生成器自动产生StopIteration异常。
3、状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行。
优点:
1、生成器的好处是延迟计算。一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据处理会非常有意义。
1 #首先生成列表,速度慢 2 x = sum([i for i in range(100000)]) 3 print(x) 4 #遵循迭代器协议的生成器,速度快 5 y = sum(i for i in range(100000)) 6 print(y)
2、生成器能够有效提高代码可读性
注意:生成器只能遍历一次
1 def get_population(): 2 with open("人口普查.txt","r+",encoding = "utf8") as f: 3 for i in f : 4 yield i 5 g = get_population() 6 n = [] 7 for i in range(4): 8 n.append(eval(g.__next__())["population"]) #生成器只能遍历一次,所以必须记录下来 9 total_population = sum(n) 10 for i in range(4): 11 print("%s %%" %(n[i]/total_population*100)) #以%形式输出
生产者消费者模型
enumerate()返回两个值,一个索引,一个内容
原始的情形:
1 def producer(): 2 ret=[] 3 for i in range(1000): 4 ret.append("包子%s"%i) 5 return ret 6 7 def consumer(res): 8 for index,baozi in enumerate(res): 9 print("第%s个顾客吃了%s"%(index,baozi)) 10 11 res = producer() 12 consumer(res)
t.send(x) 这个函数可以像__next__()方法一样调用运行下一步。
1 #不打印传递的值 2 def func1(): 3 print("start") 4 yield 1 5 print("first time") 6 yield 2 7 print("second time") 8 yield 3 9 10 t = func1() 11 print(t) 12 # <generator object func1 at 0x00000243B091F9A8> 生成器 13 y = t.send(None) 14 print(y) 15 # start 16 # 1 17 print(t.__next__()) 18 # first time 19 # 2
t.send(x) 的x会传给上次运行停止位置的yield,yield传值的用法:
1 #打印传递的值 2 def func1(): 3 print("start") 4 x = yield 1 5 print("first time",x) 6 yield 2 7 print("second time") 8 yield 3 9 t = func1() 10 print(t.__next__()) 11 # start 12 # 1 13 y = t.send(None) 14 print(y) 15 # first time None 16 # 2
在一个程序内模拟两个程序的并发(单线程的并发)
1 import time 2 def consumer(name): 3 print("[%s]开始准备吃馄饨了"%name) 4 while True: 5 huntun = yield 6 time.sleep(1) #控制吃的速度 7 print("[%s]吃第{%s}个馄饨了"%(name,huntun)) 8 def producer(): 9 c1 = consumer("Jenny") 10 c2 = consumer("Lily") 11 c1.__next__() 12 c2.__next__() 13 for i in range(1,3): 14 time.sleep(1) #控制做的速度 15 c1.send(i) 16 c2.send(i) 17 producer() 18 # [Jenny]开始准备吃馄饨了 19 # [Lily]开始准备吃馄饨了 20 # [Jenny]吃第{1}个馄饨了 21 # [Lily]吃第{1}个馄饨了 22 # [Jenny]吃第{2}个馄饨了 23 # [Lily]吃第{2}个馄饨了