生成器
# 凡是函数体内存在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、等等
# 面向过程编程
# 是一种机械式的思维方式,一步步的解决问题,以一种流程的方式编写
# 优点:复杂的问题流程化,简单化
# 缺点:可扩展性差