python全栈学习总结五:迭代器和生成器
Posted qilvzhuiche
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python全栈学习总结五:迭代器和生成器相关的知识,希望对你有一定的参考价值。
一 迭代器
1 什么是迭代器协议
迭代器协议:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么引起一个Stoplteration异常,以终止迭代(只能往后走不能往前退)
可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
2 自定义迭代器
class MyIterator: #自定义迭代器类 def __init__(self,x = 2,xmax = 63): self.__mul,self.__x = x,x self.__xmax = xmax def __iter__(self): #定义迭代器协议方法,返回类本身 return self def __next__(self):#定义迭代器协议方法 if self.__x and self.__x !=1: self.__mul *= self.__x if self.__mul <= self.__xmax: return self.__mul else : raise StopIteration else : raise StopIteration if __name__ == ‘__main__‘: myiter = MyIterator(3,150) #实例化迭代器MyIterator for i in myiter: print("迭代的数据元素为:",i)
3 可迭代对象
字符串,列表,元组,字典,集合,文件(str,list,tuple,dict,set,file)均为可迭代对象,通过for循环调用对象内部的__iter__方法,变成可迭代对象
使用的迭代器协议函数
x = ‘hell‘
iter_x = x.__iter__() #转化为可迭代对象,遵循迭代协议
iter_x.__next__() #转换后,可以使用__next__()方法来进行迭代
next(iter_x)#也可以使用next函数进行迭代
for循环的实质是在调用next方法或函数
另外两个迭代函数
iter(iterable) 参数iterable为可迭代类型
iter(callable,sentine) 参数callable是可调用类型,一般为函数,第二个参数为哨兵,当第一个参数(函数)调用返回值等于第二个参数的值时,迭代或遍历停止。
class Counter: #定义用于计数的类 def __init__(self,x=0): #定义构造函数,初始化实例属性 self.x = x counter = Counter() #初始化实例类Counter def used_iter():#定义用于iter()函数的函数 counter.x +=2 #修改计数类中的实例属性的值 return counter.x for i in iter(used_iter,8): #迭代iter()函数产生的迭代器 print("本次遍历的数值:",i)
4 for和while操作可迭代对象,输出相同
x = ‘hello‘ for i in x: print(i) x = ‘hello‘ x_iter = x.__iter__() while True: try: print(x_iter.__next__()) except StopIteration: print("迭代循环完成!") break
二 生成器
生成器:一种数据类型,自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象
1 三元表达式与列表解析
三元表达式:res = ‘good‘ if num == True else ‘bad‘ #当num为True时,res的结果为‘good’,否则res的结果为‘bad‘
列表解析:l = [i for i in range(10)] #生成列表l = [0,1,2,3,4,5,6,7,8,9]
l1 = [i for i in range(10) if i>5] #三元表达式与列表解析相结合。
2 生成器表达式创建
(1)生成器表达式:num = (i for i in range(10) if i > 5) #注意列表解析是用的[]而生成器表达式是()
#列表解析 ret_list = [] for i in range(10): ret_list.append(" 包子%s"%i) print(ret_list) ret_list = ["包子%s"%i for i in range(10)] print(ret_list) ret_list = ("包子%s"%i for i in range(10)) print(ret_list) print(ret_list.__next__()) print(ret_list.__next__()) print(ret_list.__next__())
注意观察列表解析和生成器创建的区别:列表解析一次生成列表,而生成器只是创建一个生成器对象,需要调用next方法才能访问。列表解析数据很多时,需要占用大量的内存。生成器的好处显而易见,用的时候才生成。
#生成器创建+三元判断 ret_list = ("包子%s"%i for i in range(10) if i > 5) print(ret_list) print(ret_list.__next__()) print(ret_list.__next__()) print(ret_list.__next__())
(2)生成器对象是通过yield关键字定义的函数对象,因此,生成器也是一个函数,生成器用于生成一个值的序列,以便在迭代中使用。
def myYiled(n): #定义一个生成器(函数) while n>0: print("开始生成...:") yield n #yield语句,用于返回给调用者其后表达式的值 print("完成一次...") n -= 1 if __name__ == ‘__main__‘: for i in myYiled(4): print("遍历得到的值:",i) print(‘*‘*10) my_yield = myYiled(3) print(‘已经实例化生成器对象!‘) print(my_yield.__next__()) print(‘第二次调用__next__()方法‘) print(my_yield.__next__()) print("第三次开始调用!") print(next(my_yield))
运行现象:
yield语句是生成器中的关键语句,生成器在实例化时并不会立即执行,而是等待调用其__next__()方法的时候才开始执行,并且当程序运行完yield语句后就会保持(hold)住,即保持当前状态且停止运行,等待下一次遍历时,才恢复运行!如上图!!!!
使用生成器,可以生成一个值的序列用于迭代,并且这个值的序列不是一次生成的,而是使用一个,再生成一个,的确可以使程序节约大量内存!
yield语句不仅可以使函数成为生成器和返回值,还可以接收调用者传来的数值。但值得注意的是:第一次调用生成器不能传给生成器None以外的值,否则会引发错误!使用生成的send方法传值!
def myYield(n): while n > 0: rcv = yield n #rcv用来接收调用者传来的值 n -= 1 if rcv is not None: n = rcv if __name__ == ‘__main__‘: my_yield = myYield(3) print(my_yield.__next__()) print(next(my_yield)) print(‘--‘*20) print("传给生成器一个值,重新初始化生成器!") print(my_yield.send(5)) for i in my_yield: print(i) my_yield = myYield(3) #print(my_yield.send(4)) #xxxx第一次调用就传值错误!!! print(my_yield.send(None)) #但是可以传None print(next(my_yield)) print(my_yield.send(6))
3 生成器特性:只能取一次
利用生成器来计算总人口,从文件中读取数据,分析出人口的总数量,并计算出所占比例
def get_population(): with open("人口统计",‘r‘,encoding=‘utf-8‘) as f: for p in f: yield p num_p = get_population() # print(num_p.__next__()) # print(next(num_p)) all_p = sum(int(eval(p)[‘population‘]) for p in num_p) #eval提取数据类型字典 print("总人口:%s"%all_p) num_p = get_population()#上面已经取完,重新调用生成器 for num in num_p: num_dict = eval(num) # print("%s的比例:%s%%"%(10,8)) print("%s的比例:%.2f%%"%(num_dict[‘name‘],int(num_dict[‘population‘])*100/all_p))
#人口统计文件
{"name":"济南","population":"100"} {"name":"青岛","population":"200"} {"name":"烟台","population":"300"} {"name":"潍坊","population":"400"} {"name":"菏泽","population":"500"} {"name":"临沂","population":"600"}
上面例程中,调用内置函数sum求和,使用生成器的方式计算,没有使用列表解析的方式,节省了大量内存,同理使用max,min等类似!一定要用生成器的方法!一个一个的计算!
4 生成器与协程(生产者与消费者)
运用send()方法来重置生成器的生成序列,也成为协程,是一种解决程序并发的方法!
def consumer(): #定义一个消费者模型(生成器协程) print("等待接收处理任务...") while True: data = (yield ) #模拟接收并处理任务 print(‘收到任务!‘) #此处可以执行函数调用来完成相关任务 def producer(): c = consumer() c.__next__() for i in range(5): print("发送一个任务...‘,‘任务%d"%i) c.send(‘任务%d‘%i) if __name__ == ‘__main__‘: producer()
以上是关于python全栈学习总结五:迭代器和生成器的主要内容,如果未能解决你的问题,请参考以下文章