python之路16--之迭代器与生成器
一、迭代器
1、什么是迭代器
迭代,顾名思义就是重复做一些事很多次(就现在循环中做的那样)。迭代器是实现了__next__()方法的对象(这个方法在调用时不需要任何参数),它是访问可迭代序列的一种方式,通常其从序列的第一个元素开始访问,直到所有的元素都被访问才结束。 [注意]:迭代器只能前进不能后退
2、迭代器的优点
从容器类型中一个一个的取值,会把所有的值都取到(节省内存空间)迭代器并不会在内存中在占用一大块内存,而是随着循环,每次生成一个,每次next一个给程序
3、迭代器和可迭代类型
迭代器:迭代器可以通过__next__方法从迭代器中一个一个取值,迭代器同时含有__iter__()和__next__()方法
可迭代类型:可被for循环的类型都是可迭代的,可迭代的类型都含有__iter__方法;
可迭代协议:只要含有__iter__方法的都是可迭代的,
迭代器协议:内部含有__next__和__iter__方法的就是迭代器
4、如果判断一个类型是不是迭代器
1、
from collections import Iterable from collections import Iterator print(isinstance([],Iterator)) #判断[]是不是一个迭代器 print(isinstance([],Iterable)) #判断[]是不是可迭代的类型
2、
# print('__iter__' in dir(int)) # print('__iter__' in dir(bool)) # print('__iter__' in dir(list)) # print('__iter__' in dir(dict)) # print('__iter__' in dir(tuple)) # print('__iter__' in dir(enumerate([]))) # print('__iter__' in dir(range(1))) #只要是能被for循环的数据类型就一定拥有__iter__方法,直要含有__iter__()方法就可以判断为这是一个迭代器类型
5、获取迭代器
print([].__iter__()) #一个列表执行了__iter__()之后的返回值就是一个迭代器
迭代器总结:
可以被for循环的都是可迭代的,可以迭代的内部都有__iter__方法 只要是迭代器一定可以迭代
可迭代的类型,使用.__iter__()方法就可以得到一个迭代器 ,迭代器中的__next__方法可以一个一个的获取值 ,for 循环其实就是在使用迭代器,使用print([].__iter__())打印出的结果有:类型_iterator object at XXXXX,表示为是一个迭代器即可循环类型,
只有是迭代对象的时候才能使用for循环,当遇到一个新的变量,不确定能不能for循环的时候,就判断它是否是迭代器
迭代迭代器的好处:
从容器类型中一个一个的取值,会把所有的值都取到。 节省内存空间 迭代器并不会在内存中在占用一大块内存,
而是随着循环,每次生成一个,每次next一个然后给我
二、生成器
生成器两种类型说明
1、生成器函数
2、声称其表达式
1、生成器函数
只要含有yield关键字的函数都是生成器函数,yield只能用于函数里,不可以用在函数外面,yield也和return一样可以返回一个值(不会结束代码),但是是返回的是生成器,且在同一个函数里return不可以和yield共用
1、单独获取一个生成器的值 def generator(): print(123456789) yield 'a' #yield也会将后面的值返回,但是不会结束该函数的代码 ret = generator() #执行生成器函数:执行之后会得到一个生成器作为返回值 print(ret) print(ret.__next__()) #使用.__next__()方法可以获取一次生成器的值 2、循环获取生成器的值 def generator(): #定义生成器函数 print(1) yield 'a' print(2) yield 'b' print(3) yield 'c' p = generator() #接收返回值并执行生成器函数 print(p) #打印生成器的内存地址 print(p.__next__()) #打印生成器内容到第一个yield print(p.__next__()) #打印生成器内容到第二个yield print(p.__next__()) #打印生成器内容到第三个yield #下面是循环打印 for i in p: print(i)
说明:生成器生成的代码是每次调用现生成的,不是一次性都生成在了内存里,节省内存
def wahaha(): for i in range(200): yield '易搜%s' %i g = wahaha() count = 0 for i in g: count += 1 print(i) if count > 50: #只打印50个 break
实现一个监控用户指定文件名内容的生成器代码
def tail(filename): f = open(filename,encoding='utf-8') while True: line = f.readline() if line.strip(): yield line.strip() g = tail('file') for i in g: if 'yisou.co' in i: #如果文件里包含yisou.co的字符串 print('****',i) #就打印
send的使用
def generator(): #定义一个生成器函数 print(123) p = yield 1 #返回一个生成器,并使用p接收一个值 print('----',p) yield 2 #返回第二个生成器 ret = g = generator() #执行生成器函数,并接受生成器 print(ret.__next__()) #激活生成器 print(ret.send('hello')) #执行生成器,并向上一个yield传一个值
send说明:
send获取下一个值得效果和next基本一致,
只是在获取下一个值的时候,给上一个yield的位置传递一个数据
使用send的注意事项:
第一次使用生成器的时候,一定要用__next__获取下一个值,否则会报错
最后一个yield不能接收外部的值
下面是一个计算平均数的生成器(使用到了send方法)
def average(): #定义生成器函数 sum = 0 count = 0 avg = 0 while True: num = yield avg #返回生成器 sum += num count += 1 avg = sum/count avg_g = average() #调用生成器函数,并使用avg_g变量接收生成器返回值 avg_g.__next__() #激活装饰器 avg1 = avg_g.send(10) #使用send方法调用生成器并传一个值给上一个变量 avg2 = avg_g.send(20) #使用send方法调用生成器并传一个值给上一个变量 print(avg1,'分割符',avg2) #打印计算结果
yeild from使用
yeild from 实现整体返回,单个读取
def genarator(): a = 'abcdef' b = '123456' yield from a #yield from a 整体输出,可以实现单个读取 yield from b g = genarator() for i in g: print(i)
2、生成器表达式
g = (i for i in range(10)) #g为生成器 print(g) #打印生成器 for i in g: #循环生成器 print(i)
迭代器与生成器总结
迭代器:
可迭代协议:含有__iter__方法的都是可以迭代的,可迭代的类型都可以for循环
迭代器协议:含有__next__和__iter__方法的都是迭代器
特点:节省内存空间,方便逐个取值,一个迭代器只能取一次值
生成器:
生成器函数:含有yield关键字的函数都是生成器函数
生成器函数的特点:调用之后函数内的代码不执行,返回生成器
每从生成器中取一个值就会执行一段代码,遇见yield就停止
如何从生成器中取值:1、for :如果没有break会一直到取完
2、next :每次只获取一个值
3、send :获取值时不能第一个使用send,要使用next,send取下一个值的时候给上一个位置传一个新的值
4、数据类型强制转换 :会一次性把所有数据都读取到内存里(大数据量时不要使用)
生成器表达式:
(条件成立想放在生成器中的值 for i in 可迭代的类型 if 条件)
各种推导式
了解即可
1、列表推导式的几种写法
1、 print(['鸡蛋%s' %i for i in range(10)]) 结果为:鸡蛋0 ... 鸡蛋9
2、 print([i*2 for i in range(10)]) #结果为:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3、 print([i*i for i in range(10)]) #结果为:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4、30以内所有能被3整除的数 ret = [i for i in range(30) if i%3 == 0]
5、30以内所有能被3整除的数的平方 ret = [i*i for i in range(30) if i%3 == 0]
6、找到嵌套列表中名字含有两个‘e’的所有名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] print([name for lst in names for name in lst if name.count('e') >= 2])
2、字典推导式
1、将一个字典的key和value对调 mcase = {'a': 10, 'b': 34} mcase_frequency = {mcase[k]: k for k in mcase} print(mcase_frequency) 2、合并大小写对应的value值,将k统一成小写 mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3} mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()} print(mcase_frequency)
3、集合推导式
1、计算列表中每个值的平方,自带去重功能 squared = {x**2 for x in [1, -1, 2]} print(squared) # Output: set([1, 4])
4、练习题
1、过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母 [name.upper() for name in names if len(name)>3] 2、求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表 [(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1] 3、求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]] [row[2] for row in M]
生成器练习题
1、练习题1
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)) #结果[]
总结:
生成器返回后只可以被调用一次,如果需要调用多个生成器需要依次返回多个生成器
画图说明执行步骤:
2、练习题2
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) #2、拆解上面的for循环 #当n = 1时 #执行这个g=(add(n,i) for i in g) #当n = 10时 #执行这个g=(add(n,i) for i in g) print(list(g)) #1、从这里开始执行,结果为:20,21,22,23 #执行步骤说明: def add(n,i): return n+i def test(): for i in range(4): yield i #返回生成器0,1,2,3 g = test() #g = 0,1,2,3 for n in [1,10]: g=(add(n,i) for i in g) #2、拆解上面的for循环 #当n = 1时 #执行这个g=(add(n,i) for i in g) #3、这个g=上面的0,1,2,3 #当n = 10时 #执行这个g=(add(n,i) for i in g) #2、执行这里g=(add(n,i) for i in (add(n,i) for i in g)),此时n=10,g=0,1,2,3;4、10+0,10+1,10+2,10+3,执行这步后为:g=(add(n,i) for i in (10,11,12,13));5、之前前面n+i,最终结果为:20,21,22,23 print(list(g)) #1、从这里开始执行
画图说明:
3、练习题3
def add(n,i): return n+i def test(): for i in range(4): yield i g = test() for n in [1,10,5]: g = (add(n,i) for i in g) print(list(g)) 结果为;15,16,17,18
执行步骤同练习2一样