生成器 # 凡是函数体内存在yield关键字,调用函数体不会执行函数体代码,会得到一个返回值,该返回值就是生成器对象 # 需要提示的是,生成器是一个特殊的迭代器 # next的功能就是为了触发函数体的执行 # yield可以让函数暂停在本次循环的位置,当再有next调用触发时,就会继续本次调用的位置继续往下执行,如此循环往复。 # 实例:生成器对象 # def func(): # print('first') # yield 1#1是自己定义的yied返回值 # g=func() # print(g) # 输出结果: # <generator object func at 0x004CE2A0> # 实例:生成器对象调用 # def func(): # print('first') # yield 1#1是自己定义的yield返回值 # print('second') # yield 2 # print('third') # yield 3 # g=func() #这里是一行代码都没有运行,如果想要触发生成器,就需要调用next的方法,g呢是一个函数, # # 也将会触发g函数体内的代码执行, # res1=next(g)#会触发函数的执行,直到碰到一个yield停下来,并且将yield后的值当作本次next的结果返回 # # 迭代器调用next会返回一个值,可以将值赋值给一个变量res,res只是第一次迭代返回的值 # # 过程: # #1.next防止调用 # #2.g函数体内代码执行,遇到第一个yield停止,并返回结果 # res2=next(g)#第二次# 迭代,返回res2 # 自定义一个跟内置的range函数一样的生成器 # def my_range(start,stop,step): # while start < stop: # yield start # start +=step # 取值方式1,使用迭代器,next触发函数运行,一个个的取出,要多少取多少,不会浪费内存空间, # 再大的数字也不会担心内存溢出,但是for循环取值的话,是一次性取出范围内所有的值,如果超过容器最大的范围 # 会造成内存溢出 # g=my_range(1,10,1) # res1=next(g) # print(res1) # res2=next(g) # print(res2) # res3=next(g) # print(res3) # res4=next(g) # print(res4) # 取值方式2,使用for循环 # for i in my_range(1,100000000,1): # print(i) #列表,每个元素都存在列表里 # g1=[x*x for x in range(10)] # print(g1) #生成器,是一个可以产生出列表元素的算法工具,在使用时,才能产生需要的列表元素。 # 使用方式: # 1.直接使用next()进行调用,因为生成器本身就是一个迭代器,拥有__iter__和__next__内置方法 # 2.可以使用for循环把生成器内的元素遍历出来,for循环使用的就是__iter__和__next__同样的机制 # g2 = (x*x for x in range(10)) # print(g2) #输出对比 # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # <generator object <genexpr> at 0x010EE2D0> # 案例1 #斐波那契数列: # def fib1(max): # n,a,b=0,0,1 # while n < max: # print(b) # a,b=b,a+b # n = n + 1 # return 'done'#函数的返回值,给出函数执行的最后的结果,可以不写,但是最好带上,以免产生不必要的麻烦 # fib1(10)#调用函数,执行函数体内的代码 # print(fib1.__name__)#函数名字 # print(fib1)#函数的内存地址 #改写成生成器的方式: # def fib2(max): # n,a,b=0,0,1 # while n<max: # yield b # a,b=b,a+b # n = n + 1 # return 'done' #f=fib2(10)#接收fib的生成器的返回值,yied的返回值定义的是b,下次next操作时是接着本次循环往后接续取值。 # #第一次取值 # res1=next(f) # print(res1) # #第二次取值 # res2=next(f) # print(res2) # for i in fib2(6): # print(i) # 案例2 # def odd(): # print('step 1') # yield 1 #第一次取值时,遇到yied中断,返回值为1 # print('step 2') # yield(3) #第二次取值时,继续第一次中断的地方继续往下执行, # # 遇到第二个yied时中断,完成了第二次的取值,返回值3,依次进行 # print('step 3') # yield(5) # o=odd() # next(o) #第一次 # next(o) #第二次 # next(o) #第三次 # next(o)#第四次取值时因为超出了范围就报错 # yied表达式形式的应用 # x=yield #yied 的表达式形式 # .send()#给yied传值 # 实例 # 针对yied表达式的使用, # 第一步: # 现使用next将函数停在yied的位置, # 或者使用‘,send(None)’传一个none得值给yied,如果第一次没有传空值,就会有如下报错 # def func(name): # print("%s往前跳"%name) # while True: # num=yield # print("%s往前跳了%s步"%(name,num)) # # f=func('joke') # 输出结果 # f.send(2) # f.send(2) # TypeError: can't send non-None value to a just-started generator # 第二步:使用‘.send’给yied传值 # 第三步:使用next就可以使停在yied的位置的函数进行往下运行,while循环再次停在yied的位置 # 然后再‘.send’给yied传值,往复循环,就会源源不断的有值进去。 # 注:‘,send’本身就有next的功能,当传完值后,无需使用next,即可继续往下走 # def func(name): # print("%s往前跳"%name) # while True: # num=yield # print("%s往前跳了%s步"%(name,num)) # # f=func('joke') # next(f) # f.send(1) #第一次传值,必须传空值none(next之后无需再传空值) # f.send(2) # f.send(4) # 总结: # 1.yied只能在函数内使用 # 2.yied提供了一种自定义迭代器的解决方案 # 3.yied可以保存函数的暂停状态 # 4.yied与return # 相同之处都可以返回值,值得类型和个数都没有限制 # 不同之处,yied可以返回多次值,return只能返回一次值 # 生成器表达式 # l=[1,2,3,4]#列表生成式 # l=(1,2,3,4)#生成器表达式,把[]换成()即可 # # 实例 # 注:生成器对象在没有进行next调用时是没有进行yield执行的,即不使用不触发;不next不执行 #生成器内部不存值,值是内部在每次调用时制造出来。 # # g=(i**2 for i in range(1,5) if i>3) # print(next(g))#停在第一次满足i>3后获得的i值处,i=4 # print(next(g))#第二次进行yied操作,满足条件,停在i=5的地方 # print(next(g))#第三次进行next操作的时候,已超过了范围内,就会报错,停止执行 #注:为了不导致内存溢出,使用生成器 # 实例: # 统计文件内的字符数 # 方式1 # with open('今日内容','rt',encoding='utf-8') as f: # data=f.read() # print(len(data)) # 问题:所有文本的内容全部读取到内存,如果文件过大,将会导致内存不够使用,进而导致内存溢出 # 方式2 # with open('今日内容','rt',encoding='utf-8') as f: # res=0 # for line in f: # res +=len(line) # print(res) # 问题:是没导致内存溢出,因为是一行一行的进行读取的,但是使用了for自己定义的循环,繁琐了很多 # 方式:3 # # with open('今日内容','rt',encoding='utf-8') as f: # print(sum(len(line) for line in f)) # 生成器表达式:(len(line) for line in f) #生成器对象 # 再使用sum内置函数求和sum() # 面试关于生成器实例: # def add(n,i): # return n+i # # def test(): # for i in range(4): # yield i # # g=test() # for n in [1,10]: #n=10 # g=(add(n,i) for i in g) # 分析: # 第一次循环: # n=1,没有触发生成器对象的函数体代码,整个的函数的代码,将原封不动的传给g # g=(add(n,i) for i in test()) # test中i=0,1,2,3 # n+i=10,11,12,13 # g=(10,11,12,13) # 第二次循环: # n=10时 next,触发了函数体里的代码 # g=(add(n, i) for i in (add(n,i) for i in test())) # (add(n,i) for i in test())=(10,11,12,13) # n+i=20,21,22,23 # g=(20,21,22,23) # print(n) # res=list(g) # 迭代器1=(add(n,i) for i in test()) # for i in 迭代器1: #i=next(迭代器1) i=11 # add(n, i) #add(10,11) #A. res=[10,11,12,13] #B. res=[11,12,13,14] #C. res=[20,21,22,23] #D. res=[21,22,23,24] # 答案:c # 内置函数 # 常用的内置函数,了解熟悉,用到时查看下具体的用法 # abs、max、sum、min、等等 # 面向过程编程 # 是一种机械式的思维方式,一步步的解决问题,以一种流程的方式编写 # 优点:复杂的问题流程化,简单化 # 缺点:可扩展性差