可迭代对象
字符串、列表、元祖、集合、字典都是可迭代的,数字是不可迭代的。(可以用for循环遍历取出内部元素的就是可迭代的)
如何查看一个变量是否为可迭代:
from collections import Iterable l = [1,2,3,4] t = (1,2,3,4) d = {1:2,3:4} s = {1,2,3,4} print(isinstance(l,Iterable)) print(isinstance(t,Iterable)) print(isinstance(d,Iterable)) print(isinstance(s,Iterable)) #结果为True就是可迭代,False就是不可迭代
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义就是内部实现了__iter__方法,即可迭代对象中封装有__iter__方法。
迭代器
迭代器:用变量调__iter__后就可以生成一个迭代器,迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
l = [1,2,3,4] l_iter = l.__iter__()#l_iter只是一个接受的变量 item = l_iter.__next__()#利用迭代器取值 print(item)#1 item = l_iter.__next__() print(item)#2 item = l_iter.__next__() print(item)#3 item = l_iter.__next__() print(item)#4 item = l_iter.__next__() print(item)#超出限度,报错
上步在最后出现了报错情况,为了使程序不报错,可以在取完了的最后将其终止掉:
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
生成器
生成器:(本质就是一个迭代器,不过是由程序员写出来的才叫生成器,内置的就叫迭代器)
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行,惰性。
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
简易生成器:
import time def func(): a = 1 print(‘现在定义了a变量‘) yield a b = 2 print(‘现在又定义了b变量‘) yield b g1 = func() print(‘g1 : ‘,g1) #打印g1可以发现g1就是一个生成器 print(‘-‘*20) #我是华丽的分割线 print(next(g1)) time.sleep(1) #sleep一秒看清执行过程 print(next(g1))#每print一次next才会出来一个yield的值,不然就挂在上一个yield上不继续执行
生成器有什么好处呢?就是不会一下子在内存中生成太多数据,只有在你要的时候才会给你你要的数据
生成器应用的几个小栗子:
有关衣服订单:
def produce(): """生产衣服""" for i in range(2000000): yield "生产了第%s件衣服"%i product_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break #到这里我们找工厂拿了8件衣服,我一共让我的生产函数(也就是produce生成器函数)生产2000000件衣服。 #剩下的还有很多衣服,我们可以一直拿,也可以放着等想拿的时候再拿
生成器监听文件输入的栗子:
import time def tail(filename): f = open(filename) f.seek(0, 2) #从文件末尾算起 while True: line = f.readline() # 读取文件中新的文本行 if not line: time.sleep(0.1) continue yield line tail_g = tail(‘tmp‘) for line in tail_g: print(line)
计算移动平均值(类似于年化收益):
def averager(): total = 0 day = 0 average = 0 while True: term = yield average total += term day += 1 average = total/day g_avg = averager() next(g_avg) print(g_avg.send(10)) print(g_avg.send(12)) print(g_avg.send(13))
yield from可以在实行for循环的效果的同时将代码变少:
def gen1(): for c in ‘AB‘: yield c for i in range(3): yield i print(list(gen1()))#[‘A‘,‘B‘,1,2,3] #简化版本 def gen2(): yield from ‘AB‘ yield from range(3) print(list(gen2()))#[‘A‘,‘B‘,1,2,3]
列表推导式和生成器表达式:(这里用一个小故事讲解知识点)
#为了彰显高富帅本质,一口气买了十个茶叶蛋,将他们依次排开并编号,拍照发到朋友圈 egg_list=[‘茶叶蛋%s‘ %i for i in range(10)] #列表解析 #可是这十个茶叶蛋一口气吃不完啊,要吃也就是一个一个吃,那么就吃一个拍一个照吧 eat=(‘茶叶蛋%s‘ %i for i in range(10))#生成器表达式 print(eat) print(next(eat)) #next本质就是调用__next__ print(eat.__next__()) print(next(eat))
高富帅与茶叶蛋
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。
附:与生成器相关的面试题:
def demo(): for i in range(4): yield i g=demo() g1=(i for i in g) g2=(i for i in g1) print(list(g1))#[0,1,2,3] print(list(g2))#[]
面试题1
def add(n,i): return n+i def test(): for i in range(4): yield i g=test() for n in [1,10]: g=(add(n,i) for i in g) print(list(g))#[20,21,22,23]
面试题2
what's the python之可迭代对象、迭代器与生成器(附面试题)
原文地址:https://www.cnblogs.com/zhuminghui/p/8206439.html