迭代器 与 生成器
""" 什么是迭代器 迭代:更新换代(重复)的过程,每次的迭代都必须基于上一次的结果 迭代器:迭代取值的工具 为什么要用 迭代器给你提供了一种不依赖于索引取值的方式 如何用 """ 不算 因为只是简单的重复 n = 0 while True: print(n) 重复 + 每次迭代都是基于上一次的结果而来的 l = [1,2,3,4] s = 'hello' n = 0 while n < len(s): print(s[n]) n += 1 """ 需要迭代取值的数据类型 字符串 列表 元组 字典 集合 """ 可迭代对象 只有内置有__iter__方法的都叫做可迭代对象 """ 补充:针对双下线开头双下划线结尾的方法 推荐读:双下+方法名 基本数据类型中 是可迭代对象的有 str list tuple dict set 文件对象(执行内置的__iter__之后还是本身 没有任何变化):文件对象本身就是迭代器对象 """ n = 1 f = 1.1 s = 'hello' l = [1,2,34,] t = (1,2,34) s1 = {1,2,3,4} d = {'name':'jason'} f1 = open('xxx.txt','w',encoding='utf-8') res = s.__iter__() res = iter(s) print(s.__len__()) 简化成了len(s) res1 = l.__iter__() res1 = iter(l) res2 = f1.__iter__() res2 = iter(f1) print(res,res1,res2) print(f1) 可迭代对象执行内置的__iter__方法得到就是该对象的迭代器对象 """ 迭代器对象 1.内置有__iter__方法 2.内置有__next__方法 ps:迭代器一定是可迭代对象 而可迭代对象不一定是迭代器对象 """ l = [1,2,3,4] 生成一个迭代器对象 iter_l = l.__iter__() 迭代器取值 调用__next__ print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) print(iter_l.__next__()) 如果取完了 直接报错 d = {'name':'jason','password':'123','hobby':'泡m'} 将可迭代对象d转换成迭代器对象 iter_d = d.__iter__() 迭代器对象的取值 必须用__next__ print(iter_d.__next__()) print(iter_d.__next__()) print(iter_d.__next__()) print(iter_d.__next__()) 取完了 报错StopIteration f1 = open('xxx.txt','r',encoding='utf-8') 调用f1内置的__iter__方法 iter_f = f1.__iter__() print(iter_f is f1) """ 迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身(******) """ print(f1 is f1.__iter__().__iter__().__iter__().__iter__()) """ 问:__iter__方法就是用来帮我们生成迭代器对象 而文件对象本身就是迭代器对象,为什么还内置有__iter__方法??? """ d = {'name':'jason','password':'123','hobby':'泡m'} iter_d = d.__iter__() print(d.__iter__().__next__()) print(d.__iter__().__next__()) print(d.__iter__().__next__()) print(iter_d.__next__()) print(iter_d.__next__()) print(iter_d.__next__()) print(iter_d.__next__()) 异常处理 while True: try: print(iter_d.__next__()) except StopIteration: print('老母猪生不动了') break f = open('xxx.txt','r',encoding='utf-8') iter_f = f.__iter__() print(iter_f.__next__()) print(iter_f.__next__()) print(iter_f.__next__()) """ 迭代器取值的特点 1.只能往后依次取 不能后退 """ d = {'name':'jason','password':'123','hobby':'泡m'} for i in d: print(i) for循环后面的in关键 跟的是一个可迭代对象 """ for循环内部的本质 1.将in后面的对象调用__iter__转换成迭代器对象 2.调用__next__迭代取值 3.内部有异常捕获StopIteration,当__next__报这个错 自动结束循环 """ for i in 1: pass iter(1) """ 可迭代对象:内置有__iter__方法的 迭代器对象:既内置有__iter__也内置有__next__方法 迭代取值: 优点 1.不依赖于索引取值 2.内存中永远只占一份空间,不会导致内存溢出 缺点 1.不能够获取指定的元素 2.取完之后会报StopIteration错 """ l = [1,2,3,4] res = map(lambda x:x+1,l) print(map(lambda x:x+1,l)) print(res.__next__()) print(res.__next__()) print(res.__next__()) l1 = [1,2,3,4,5] l2 = ['a','b','c'] print(zip(l1,l2)) """ 生成器:用户自定义的迭代器,本质就是迭代器 """ def func(): print('first') yield 666 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 print('second') yield 777 print('third') yield 888 print('forth') yield yield yield后面跟的值就是调用迭代器__next__方法你能得到的值 yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回 g = func() 生成器初始化:将函数变成迭代器 print(g) print(g.__next__()) print(g.__next__()) print(range(1,10)) for i in range(1,10,2): print(i) def my_range(start,end,step=1): while start < end: yield start start += step for j in my_range(1,100,2): print(j) yield支持外界为其传参 def dog(name): print('%s 准备开吃'%name) while True: food = yield print('%s 吃了 %s'%(name,food)) def index(): pass 当函数内有yield关键字的时候,调用该函数不会执行函数体代码 而是将函数变成生成器 g = dog('egon') g.__next__() 必须先将代码运行至yield 才能够为其传值 g.send('狗不理包子') 给yield左边的变量传参 触发了__next__方法 g.send('饺子') """ yield 1.帮你提供了一种自定义生成器方式 2.会帮你将函数的运行状态暂停住 3.可以返回值 与return之间异同点 相同点:都可以返回值,并且都可以返回多个 不同点: yield可以返回多次值,而return只能返回一次函数立即结束 yield还可以接受外部传入的值 """ 列表生成式 res = [i for i in range(1,10) if i != 4] print(res) res = (i for i in range(1,100000000) if i != 4) 生成器表达式 print(res) """ 生成器不会主动执行任何一行代码 必须通过__next__触发代码的运行 """ print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) 一次全部读取整个文件占内存 f = open('xxx.txt','r',encoding='utf-8') data = f.read() print(len(data)) f.close() with open('xxx.txt','r',encoding='utf-8') as f: n = 0 for line in f: n += len(line) print(n) g = (len(line) for line in f) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(sum(g)) 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) 第一次for循环g=(add(n,i) for i in test()) 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) print(n) res=list(g) """ for i in (add(10,i) for i in test()): 会执行所有的生成器内部的代码 add(n,i) """ A. res=[10,11,12,13] B. res=[11,12,13,14] C. res=[20,21,22,23] 答案 D. res=[21,22,23,24] print(abs(-11.11)) 求绝对值 l = [0,1,0] print(all(l)) 只要有一个为False就返回False print(any(l)) 只要有一个位True就返回True def index(): username = '我是局部名称空间里面的username' print(locals()) 当前语句在哪个位置 就会返回哪个位置所存储的所有的名字 print(globals()) 无论在哪 查看的都是全局名称空间 index() print(bin(10)) print(oct(10)) print(hex(10)) print(int('0b1010',2)) print(bool(1)) print(bool(0)) s = 'hello' print(s.encode('utf-8')) print(bytes(s,encoding='utf-8')) 可调用的(可以加括号执行相应功能的) l = [1,2,3] def index(): pass print(callable(l)) print(callable(index)) print(chr(97)) 将数字转换成ascii码表对应的字符 print(ord('a')) 将字符按照ascii表转成对应的数 dir获取当前对象名称空间里面的名字 l = [1,2,3] print(dir(l)) import test print(dir(test)) print(test.name) divmod 分页器 print(divmod(101,10)) total_num,more = divmod(900,11) if more: total_num += 1 print('总页数:',total_num) enumerate 枚举 l = ['a','b'] for i,j in enumerate(l,1): print(i,j) eval exec s = """ print('hello baby~') x = 1 y = 2 print(x + y) """ eval(s) exec(s) eval不支持逻辑代码,只支持一些简单的python代码 s1 = """ print(1 + 2) for i in range(10): print(i) """ eval(s1) exec(s1) name = 'jason' s2 = """ name """ print(eval(s2)) format 三种玩法 {}占位 {index} 索引 {name} 指名道姓 print(globals()) def login(): """ 一起嗨皮 :return: """ print(help(login)) isinstance 后面统一改方法判断对象是否属于某个数据类型 n = 1 print(type(n)) print(isinstance(n,list)) 判断对象是否属于某个数据类型 print(pow(2,3)) print(round(3.4)) """ 面向过程编程:就类似于设计一条流水线 好处: 将复杂的问题流程化 从而简单化 坏处: 可扩展性较差 一旦需要修改 整体都会受到影响 """
迭代器示例
import sys def fibonacci(n): # 生成器函数 - 斐波那契 a, b, counter = 0, 1, 0 while True: if (counter > n): return yield a a, b = b, a + b counter += 1 f = fibonacci(10) # f 是一个迭代器,由生成器返回生成 while True: try: print (next(f), end=" ") except StopIteration: sys.exit()
class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): if self.a <= 20: x = self.a self.a += 1 return x else: raise StopIteration myclass = MyNumbers() myiter = iter(myclass) for x in myiter: print(x)
总结 :
迭代器 和生成器 其实都是一种类型,既都可以理解为 一个 按钮,你按一下 出一次结果,以此来达到节省内存的效果,注意的是 ,当 yeild被赋值时,需要考虑形参的传递,函数如果含有yeild 那么 函数名()并不会直接运行函数,而是生成器的初始化,然后我们可以 不断的用函数名__next__()的方法 获取yeild右边的返回值,随后yeild的左边的值会被传入下一次的函数运行中.