Python学习(九)—— 迭代器和生成器

递归和迭代

迭代:迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值

迭代器协议

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}个馄饨了

猜你喜欢

转载自www.cnblogs.com/jennifer224/p/12380124.html