Python进阶-V 迭代器(Iterator)、生成器(Generator)函数
一、迭代器
1、可循环的有哪些,即可用for语句或者while语句的数据类型有哪些?
字符串(str)、列表(list)、元组(tuple)、字典(dic)、集合(set)、枚举类(enumerate)
还有哪些非数据类型,但是可以循环的?
range(10), 文件句柄:f = open('filename',mode='r',enconding='utf-8')
2、查看这些可循环的数据类型或者函数或者文件句柄
都有哪些共同的东西:(求交集,想到集合(set)的操作了)。
还要引入一个内置函数dir()
print(dir()) print(dir(' '))
由返回结果可以看到,dir带参数时,返回该参数的属性、方法列表。
不带参数时,返回当前范围内的变量、方法和定义的类型列表;
1 # 先可以求list和str的交集方法了 2 str_method_li = set(dir('')) 3 list_method_li = set(dir([])) 4 res = str_method_li & list_method_li 5 print(res) 6 # 还是由很多共有的方法,加大力度 7 tuple_m_li = set(dir(())) 8 dic_m_li = set(dir({})) 9 range_m_li = set(dir(range(5))) 10 set_m_li = set(dir(set({1, 2, 3, 4}))) 11 res = res & tuple_m_li & dic_m_li & range_m_li & set_m_li 12 print(res) 13 # 还是由很多,我们还是看一看交集中一个双下方法名:__iter__ 14 # 补充说明一下“双下方法”:方法名中开头和结果都带有两个下滑线的,即叫双下方法!它是python中用C实现的方法! 15 # 这个__iter__, 跟可迭代对应的英文iterable很像!
我们可以得出结论:
只要是能被for循环的数据类型,就一定拥有__iter__方法!
print([].__iter__()) # 运行结果为:<list_iterator object at 0x000001C2AD37B4A8> # 即得到list的迭代器对象,因此我们可以判断,该方法返回的是一个迭代器(iterator)! iterator = 'lov'.__iter__() print(dir(iterator)) # 看到迭代器中的方法中,有一个双下方法:__next__,我们来看看它得到的是什么? print(iterator.__next__()) # 返回的是字符串中的第一字母,再执行一次! print(iterator.__next__()) # 返回的是字符串中的第二字母,再执行一次! print(iterator.__next__()) # 返回的是字符串中的第三字母,再执行一次! #print(iterator.__next__()) # 报错 StopIteration # 这说明迭代器可以一个一个取集合中的元素,只到没有时,抛出异常!
总结一下:
#Iterable 可迭代的 -- > __iter__ #只要含有__iter__方法的都是可迭代的
[].__iter__() 迭代器 -- > __next__ #通过next就可以从迭代器中一个一个的取值
延申一下:
只要含有__iter__方法的都是可迭代的 —— 可迭代协议
自定义一个类型,可以用来迭代
1 from collections import Iterable 2 from collections import Iterator 3 4 class A: 5 def __next__(self):pass 6 def __iter__(self):pass 7 8 a = A() 9 print(isinstance(a, Iterable)) 10 print(isinstance(a, Iterator))
结果都为True,说明该类即可被迭代,也是迭代器!
3、迭代器的概念
迭代器协议:内部含有__next__方法和__iter__方法的就是迭代器
小结一下:
迭代器协议和可迭代协议
可以被for循环的都是可迭代的
可迭代的内部都有__iter__方法
只要是迭代器 一定可迭代
可迭代的.__iter__()方法就可以得到一个迭代器
迭代器中的__next__()方法可以一个一个的获取值
我们可以推导一下, for循环其实就是在使用迭代器
只有是可迭代对象的时候 才能用for
当我们遇到一个新的变量,不确定能不能for循环的时候,就判断它是否可迭代
4、迭代器的好处
从容器中一个一个取值,会将所有的值取到;
节省内存空间:
迭代器不会在内存中再占用一大块内存;
每次next,都会给我们一个新的
二、生成器函数
1、生成器的引入场景
#如果想要一个包含2百万个ILOVEU的字符串,如何取得?如此取只会得到最后一个def func(): for i in range(2000000): i = 'ILOVEU%s'%i return i print(func())
改进一下:
def generate_str(): li = [] for i in range(200): s = 'ILOVEU No' + str(i) + ' ' # print(s) li.append(s) return ','.join(li) print(generate_str())
如此做的确可以生成一个包含200万个’ILOVEU No%d‘的字符串,
但是如此使用迭代器无优势!因为它一次性在内存中生成,浪费内存空间,而迭代器的优点恰恰是节省内存!
总结:
理清迭代器的原理,可以理解for循环的是如何工作的,另外可以对了解生成器做铺垫!
迭代器虽然有很多优点,如节省内存空间,但是不能满足我们的所有需求;
如果我们平时在写代码过程中,要产生大量的数据,又不希望一次性在内存中生成,而且还要处处使用它,基于此种情况,
必须有我们自己写的迭代器!它就是生成器!
2、生成器有两种表现形式:
1)、生成器函数 --- 本质上就是我们自己写的函数
2)、生成器表达式
3、生成器函数的定义:
只要含有yield关键字的函数都是生成器函数
1 ef generator(): 2 print(1) 3 #return 'a' 4 yield 'b' # yield不能和return共用且需要写在函数内 5 6 res = generator() # 生成器函数 : 执行之后会得到一个生成器作为返回值 7 print(res) # <generator object generator at 0x0000019C239AD4C0> 8 9 def generator_full(): 10 print(1) 11 yield 'a' # yield不会结束函数 12 print(2) 13 yield 'b' 14 yield 'c' 15 g = generator_full() 16 print(dir(g)) # 它有'__iter__' 17 18 ret = g.__next__() 19 print(ret) # 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3) print(1);4)执行:yield 'a' 返回a,但不退出函数,等待 20 ret = g.__next__() 21 print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3) print(2); 4)yield 'b' 返回b,但不退出函数,等待 22 # ret = g.__next__() 23 # print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3)yield 'c' 返回c,但不退出函数,等待 24 # ret = g.__next__() 25 # print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3)发现没有yield,报错StopIteration 26 27 for i in g: # 接着上面的继续迭代剩下的元素 28 print(i) 29 30 # 用for循环迭代后,无法再迭代了! 31 # ret = g.__next__() 32 # print(ret)
4、使用生成器函数来生成200万个“ILOVEU No%d”
def s_generator(): for i in range(200): yield 'ILOVEU No%d'%i s_g = s_generator() # for i in s_g: # print(i)
继续深入
1 #需求:我要取出前50个ILOVEU 2 for i in range(50): 3 print(s_g.__next__()) 4 5 i = 0 6 # 发现是按序号接着取元素,从ILOVEU No50开始取 7 while i < 50: 8 i += 1 9 print('----',s_g.__next__()) 10 # 生成器与迭代器一样,会记录当前取到元素的位置,以及下一个元素的位置,随时都可以得到下一个元素! 11 12 # 对比for循环列表(list,不是迭代器,但是可以迭代) 13 li = [2, 4, 6, 8, 10] 14 for i in li: 15 print(i) 16 if i == 6: 17 break 18 19 for j in li: 20 print(j) 21 # 我们发现,没有从停止的位置的下一个位置取元素,而是重新取出所有元素! 22 # 原因不是list不是迭代器,for循环时,已经将其转换为迭代器, 23 # 而是,两次for的时候的迭代器不是同一个! 24 25 #我们再来看生成器s_generator的例子 26 g1 = s_generator() 27 g2 = s_generator() 28 print(g1.__next__()) 29 print(g2.__next__()) 30 # 结果都是ILOVEU No0,说明同一个生成器(同时也是迭代器),__next__时才会记住元素位置
5、监听文件输入的内容,实时打印到命令行中
#普通方法: def trip(file_path): f = open(file_path, encoding='utf-8') while 1: line = f.readline() # 每次读一行 if line.strip(): print(line.strip()) # 缺点:不能返回line,如果return,循环就终止了 #trip('../day12_func/user_info') def monitor(file_path): f = open(file_path, mode='r', encoding='utf-8') while 1: line = f.readline() if line.strip(): yield line.strip() g = monitor('../day12_func/user_info') for i in g: if 'python' in i: print('*****', i) ##修改文件后,按ctrl+s才能有效果!打印结果!

浙公网安备 33010602011771号